roojs-ui.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13
14 /*
15  * These classes are derivatives of the similarly named classes in the YUI Library.
16  * The original license:
17  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18  * Code licensed under the BSD License:
19  * http://developer.yahoo.net/yui/license.txt
20  */
21
22 (function() {
23
24 var Event=Roo.EventManager;
25 var Dom=Roo.lib.Dom;
26
27 /**
28  * @class Roo.dd.DragDrop
29  * @extends Roo.util.Observable
30  * Defines the interface and base operation of items that that can be
31  * dragged or can be drop targets.  It was designed to be extended, overriding
32  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
33  * Up to three html elements can be associated with a DragDrop instance:
34  * <ul>
35  * <li>linked element: the element that is passed into the constructor.
36  * This is the element which defines the boundaries for interaction with
37  * other DragDrop objects.</li>
38  * <li>handle element(s): The drag operation only occurs if the element that
39  * was clicked matches a handle element.  By default this is the linked
40  * element, but there are times that you will want only a portion of the
41  * linked element to initiate the drag operation, and the setHandleElId()
42  * method provides a way to define this.</li>
43  * <li>drag element: this represents the element that would be moved along
44  * with the cursor during a drag operation.  By default, this is the linked
45  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
46  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
47  * </li>
48  * </ul>
49  * This class should not be instantiated until the onload event to ensure that
50  * the associated elements are available.
51  * The following would define a DragDrop obj that would interact with any
52  * other DragDrop obj in the "group1" group:
53  * <pre>
54  *  dd = new Roo.dd.DragDrop("div1", "group1");
55  * </pre>
56  * Since none of the event handlers have been implemented, nothing would
57  * actually happen if you were to run the code above.  Normally you would
58  * override this class or one of the default implementations, but you can
59  * also override the methods you want on an instance of the class...
60  * <pre>
61  *  dd.onDragDrop = function(e, id) {
62  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
63  *  }
64  * </pre>
65  * @constructor
66  * @param {String} id of the element that is linked to this instance
67  * @param {String} sGroup the group of related DragDrop objects
68  * @param {object} config an object containing configurable attributes
69  *                Valid properties for DragDrop:
70  *                    padding, isTarget, maintainOffset, primaryButtonOnly
71  */
72 Roo.dd.DragDrop = function(id, sGroup, config) {
73     if (id) {
74         this.init(id, sGroup, config);
75     }
76     
77 };
78
79 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
80
81     /**
82      * The id of the element associated with this object.  This is what we
83      * refer to as the "linked element" because the size and position of
84      * this element is used to determine when the drag and drop objects have
85      * interacted.
86      * @property id
87      * @type String
88      */
89     id: null,
90
91     /**
92      * Configuration attributes passed into the constructor
93      * @property config
94      * @type object
95      */
96     config: null,
97
98     /**
99      * The id of the element that will be dragged.  By default this is same
100      * as the linked element , but could be changed to another element. Ex:
101      * Roo.dd.DDProxy
102      * @property dragElId
103      * @type String
104      * @private
105      */
106     dragElId: null,
107
108     /**
109      * the id of the element that initiates the drag operation.  By default
110      * this is the linked element, but could be changed to be a child of this
111      * element.  This lets us do things like only starting the drag when the
112      * header element within the linked html element is clicked.
113      * @property handleElId
114      * @type String
115      * @private
116      */
117     handleElId: null,
118
119     /**
120      * An associative array of HTML tags that will be ignored if clicked.
121      * @property invalidHandleTypes
122      * @type {string: string}
123      */
124     invalidHandleTypes: null,
125
126     /**
127      * An associative array of ids for elements that will be ignored if clicked
128      * @property invalidHandleIds
129      * @type {string: string}
130      */
131     invalidHandleIds: null,
132
133     /**
134      * An indexted array of css class names for elements that will be ignored
135      * if clicked.
136      * @property invalidHandleClasses
137      * @type string[]
138      */
139     invalidHandleClasses: null,
140
141     /**
142      * The linked element's absolute X position at the time the drag was
143      * started
144      * @property startPageX
145      * @type int
146      * @private
147      */
148     startPageX: 0,
149
150     /**
151      * The linked element's absolute X position at the time the drag was
152      * started
153      * @property startPageY
154      * @type int
155      * @private
156      */
157     startPageY: 0,
158
159     /**
160      * The group defines a logical collection of DragDrop objects that are
161      * related.  Instances only get events when interacting with other
162      * DragDrop object in the same group.  This lets us define multiple
163      * groups using a single DragDrop subclass if we want.
164      * @property groups
165      * @type {string: string}
166      */
167     groups: null,
168
169     /**
170      * Individual drag/drop instances can be locked.  This will prevent
171      * onmousedown start drag.
172      * @property locked
173      * @type boolean
174      * @private
175      */
176     locked: false,
177
178     /**
179      * Lock this instance
180      * @method lock
181      */
182     lock: function() { this.locked = true; },
183
184     /**
185      * Unlock this instace
186      * @method unlock
187      */
188     unlock: function() { this.locked = false; },
189
190     /**
191      * By default, all insances can be a drop target.  This can be disabled by
192      * setting isTarget to false.
193      * @method isTarget
194      * @type boolean
195      */
196     isTarget: true,
197
198     /**
199      * The padding configured for this drag and drop object for calculating
200      * the drop zone intersection with this object.
201      * @method padding
202      * @type int[]
203      */
204     padding: null,
205
206     /**
207      * Cached reference to the linked element
208      * @property _domRef
209      * @private
210      */
211     _domRef: null,
212
213     /**
214      * Internal typeof flag
215      * @property __ygDragDrop
216      * @private
217      */
218     __ygDragDrop: true,
219
220     /**
221      * Set to true when horizontal contraints are applied
222      * @property constrainX
223      * @type boolean
224      * @private
225      */
226     constrainX: false,
227
228     /**
229      * Set to true when vertical contraints are applied
230      * @property constrainY
231      * @type boolean
232      * @private
233      */
234     constrainY: false,
235
236     /**
237      * The left constraint
238      * @property minX
239      * @type int
240      * @private
241      */
242     minX: 0,
243
244     /**
245      * The right constraint
246      * @property maxX
247      * @type int
248      * @private
249      */
250     maxX: 0,
251
252     /**
253      * The up constraint
254      * @property minY
255      * @type int
256      * @type int
257      * @private
258      */
259     minY: 0,
260
261     /**
262      * The down constraint
263      * @property maxY
264      * @type int
265      * @private
266      */
267     maxY: 0,
268
269     /**
270      * Maintain offsets when we resetconstraints.  Set to true when you want
271      * the position of the element relative to its parent to stay the same
272      * when the page changes
273      *
274      * @property maintainOffset
275      * @type boolean
276      */
277     maintainOffset: false,
278
279     /**
280      * Array of pixel locations the element will snap to if we specified a
281      * horizontal graduation/interval.  This array is generated automatically
282      * when you define a tick interval.
283      * @property xTicks
284      * @type int[]
285      */
286     xTicks: null,
287
288     /**
289      * Array of pixel locations the element will snap to if we specified a
290      * vertical graduation/interval.  This array is generated automatically
291      * when you define a tick interval.
292      * @property yTicks
293      * @type int[]
294      */
295     yTicks: null,
296
297     /**
298      * By default the drag and drop instance will only respond to the primary
299      * button click (left button for a right-handed mouse).  Set to true to
300      * allow drag and drop to start with any mouse click that is propogated
301      * by the browser
302      * @property primaryButtonOnly
303      * @type boolean
304      */
305     primaryButtonOnly: true,
306
307     /**
308      * The availabe property is false until the linked dom element is accessible.
309      * @property available
310      * @type boolean
311      */
312     available: false,
313
314     /**
315      * By default, drags can only be initiated if the mousedown occurs in the
316      * region the linked element is.  This is done in part to work around a
317      * bug in some browsers that mis-report the mousedown if the previous
318      * mouseup happened outside of the window.  This property is set to true
319      * if outer handles are defined.
320      *
321      * @property hasOuterHandles
322      * @type boolean
323      * @default false
324      */
325     hasOuterHandles: false,
326
327     /**
328      * Code that executes immediately before the startDrag event
329      * @method b4StartDrag
330      * @private
331      */
332     b4StartDrag: function(x, y) { },
333
334     /**
335      * Abstract method called after a drag/drop object is clicked
336      * and the drag or mousedown time thresholds have beeen met.
337      * @method startDrag
338      * @param {int} X click location
339      * @param {int} Y click location
340      */
341     startDrag: function(x, y) { /* override this */ },
342
343     /**
344      * Code that executes immediately before the onDrag event
345      * @method b4Drag
346      * @private
347      */
348     b4Drag: function(e) { },
349
350     /**
351      * Abstract method called during the onMouseMove event while dragging an
352      * object.
353      * @method onDrag
354      * @param {Event} e the mousemove event
355      */
356     onDrag: function(e) { /* override this */ },
357
358     /**
359      * Abstract method called when this element fist begins hovering over
360      * another DragDrop obj
361      * @method onDragEnter
362      * @param {Event} e the mousemove event
363      * @param {String|DragDrop[]} id In POINT mode, the element
364      * id this is hovering over.  In INTERSECT mode, an array of one or more
365      * dragdrop items being hovered over.
366      */
367     onDragEnter: function(e, id) { /* override this */ },
368
369     /**
370      * Code that executes immediately before the onDragOver event
371      * @method b4DragOver
372      * @private
373      */
374     b4DragOver: function(e) { },
375
376     /**
377      * Abstract method called when this element is hovering over another
378      * DragDrop obj
379      * @method onDragOver
380      * @param {Event} e the mousemove event
381      * @param {String|DragDrop[]} id In POINT mode, the element
382      * id this is hovering over.  In INTERSECT mode, an array of dd items
383      * being hovered over.
384      */
385     onDragOver: function(e, id) { /* override this */ },
386
387     /**
388      * Code that executes immediately before the onDragOut event
389      * @method b4DragOut
390      * @private
391      */
392     b4DragOut: function(e) { },
393
394     /**
395      * Abstract method called when we are no longer hovering over an element
396      * @method onDragOut
397      * @param {Event} e the mousemove event
398      * @param {String|DragDrop[]} id In POINT mode, the element
399      * id this was hovering over.  In INTERSECT mode, an array of dd items
400      * that the mouse is no longer over.
401      */
402     onDragOut: function(e, id) { /* override this */ },
403
404     /**
405      * Code that executes immediately before the onDragDrop event
406      * @method b4DragDrop
407      * @private
408      */
409     b4DragDrop: function(e) { },
410
411     /**
412      * Abstract method called when this item is dropped on another DragDrop
413      * obj
414      * @method onDragDrop
415      * @param {Event} e the mouseup event
416      * @param {String|DragDrop[]} id In POINT mode, the element
417      * id this was dropped on.  In INTERSECT mode, an array of dd items this
418      * was dropped on.
419      */
420     onDragDrop: function(e, id) { /* override this */ },
421
422     /**
423      * Abstract method called when this item is dropped on an area with no
424      * drop target
425      * @method onInvalidDrop
426      * @param {Event} e the mouseup event
427      */
428     onInvalidDrop: function(e) { /* override this */ },
429
430     /**
431      * Code that executes immediately before the endDrag event
432      * @method b4EndDrag
433      * @private
434      */
435     b4EndDrag: function(e) { },
436
437     /**
438      * Fired when we are done dragging the object
439      * @method endDrag
440      * @param {Event} e the mouseup event
441      */
442     endDrag: function(e) { /* override this */ },
443
444     /**
445      * Code executed immediately before the onMouseDown event
446      * @method b4MouseDown
447      * @param {Event} e the mousedown event
448      * @private
449      */
450     b4MouseDown: function(e) {  },
451
452     /**
453      * Event handler that fires when a drag/drop obj gets a mousedown
454      * @method onMouseDown
455      * @param {Event} e the mousedown event
456      */
457     onMouseDown: function(e) { /* override this */ },
458
459     /**
460      * Event handler that fires when a drag/drop obj gets a mouseup
461      * @method onMouseUp
462      * @param {Event} e the mouseup event
463      */
464     onMouseUp: function(e) { /* override this */ },
465
466     /**
467      * Override the onAvailable method to do what is needed after the initial
468      * position was determined.
469      * @method onAvailable
470      */
471     onAvailable: function () {
472     },
473
474     /*
475      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
476      * @type Object
477      */
478     defaultPadding : {left:0, right:0, top:0, bottom:0},
479
480     /*
481      * Initializes the drag drop object's constraints to restrict movement to a certain element.
482  *
483  * Usage:
484  <pre><code>
485  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
486                 { dragElId: "existingProxyDiv" });
487  dd.startDrag = function(){
488      this.constrainTo("parent-id");
489  };
490  </code></pre>
491  * Or you can initalize it using the {@link Roo.Element} object:
492  <pre><code>
493  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
494      startDrag : function(){
495          this.constrainTo("parent-id");
496      }
497  });
498  </code></pre>
499      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
500      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
501      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
502      * an object containing the sides to pad. For example: {right:10, bottom:10}
503      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
504      */
505     constrainTo : function(constrainTo, pad, inContent){
506         if(typeof pad == "number"){
507             pad = {left: pad, right:pad, top:pad, bottom:pad};
508         }
509         pad = pad || this.defaultPadding;
510         var b = Roo.get(this.getEl()).getBox();
511         var ce = Roo.get(constrainTo);
512         var s = ce.getScroll();
513         var c, cd = ce.dom;
514         if(cd == document.body){
515             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
516         }else{
517             xy = ce.getXY();
518             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
519         }
520
521
522         var topSpace = b.y - c.y;
523         var leftSpace = b.x - c.x;
524
525         this.resetConstraints();
526         this.setXConstraint(leftSpace - (pad.left||0), // left
527                 c.width - leftSpace - b.width - (pad.right||0) //right
528         );
529         this.setYConstraint(topSpace - (pad.top||0), //top
530                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
531         );
532     },
533
534     /**
535      * Returns a reference to the linked element
536      * @method getEl
537      * @return {HTMLElement} the html element
538      */
539     getEl: function() {
540         if (!this._domRef) {
541             this._domRef = Roo.getDom(this.id);
542         }
543
544         return this._domRef;
545     },
546
547     /**
548      * Returns a reference to the actual element to drag.  By default this is
549      * the same as the html element, but it can be assigned to another
550      * element. An example of this can be found in Roo.dd.DDProxy
551      * @method getDragEl
552      * @return {HTMLElement} the html element
553      */
554     getDragEl: function() {
555         return Roo.getDom(this.dragElId);
556     },
557
558     /**
559      * Sets up the DragDrop object.  Must be called in the constructor of any
560      * Roo.dd.DragDrop subclass
561      * @method init
562      * @param id the id of the linked element
563      * @param {String} sGroup the group of related items
564      * @param {object} config configuration attributes
565      */
566     init: function(id, sGroup, config) {
567         this.initTarget(id, sGroup, config);
568         if (!Roo.isTouch) {
569             Event.on(this.id, "mousedown", this.handleMouseDown, this);
570         }
571         Event.on(this.id, "touchstart", this.handleMouseDown, this);
572         // Event.on(this.id, "selectstart", Event.preventDefault);
573     },
574
575     /**
576      * Initializes Targeting functionality only... the object does not
577      * get a mousedown handler.
578      * @method initTarget
579      * @param id the id of the linked element
580      * @param {String} sGroup the group of related items
581      * @param {object} config configuration attributes
582      */
583     initTarget: function(id, sGroup, config) {
584
585         // configuration attributes
586         this.config = config || {};
587
588         // create a local reference to the drag and drop manager
589         this.DDM = Roo.dd.DDM;
590         // initialize the groups array
591         this.groups = {};
592
593         // assume that we have an element reference instead of an id if the
594         // parameter is not a string
595         if (typeof id !== "string") {
596             id = Roo.id(id);
597         }
598
599         // set the id
600         this.id = id;
601
602         // add to an interaction group
603         this.addToGroup((sGroup) ? sGroup : "default");
604
605         // We don't want to register this as the handle with the manager
606         // so we just set the id rather than calling the setter.
607         this.handleElId = id;
608
609         // the linked element is the element that gets dragged by default
610         this.setDragElId(id);
611
612         // by default, clicked anchors will not start drag operations.
613         this.invalidHandleTypes = { A: "A" };
614         this.invalidHandleIds = {};
615         this.invalidHandleClasses = [];
616
617         this.applyConfig();
618
619         this.handleOnAvailable();
620     },
621
622     /**
623      * Applies the configuration parameters that were passed into the constructor.
624      * This is supposed to happen at each level through the inheritance chain.  So
625      * a DDProxy implentation will execute apply config on DDProxy, DD, and
626      * DragDrop in order to get all of the parameters that are available in
627      * each object.
628      * @method applyConfig
629      */
630     applyConfig: function() {
631
632         // configurable properties:
633         //    padding, isTarget, maintainOffset, primaryButtonOnly
634         this.padding           = this.config.padding || [0, 0, 0, 0];
635         this.isTarget          = (this.config.isTarget !== false);
636         this.maintainOffset    = (this.config.maintainOffset);
637         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
638
639     },
640
641     /**
642      * Executed when the linked element is available
643      * @method handleOnAvailable
644      * @private
645      */
646     handleOnAvailable: function() {
647         this.available = true;
648         this.resetConstraints();
649         this.onAvailable();
650     },
651
652      /**
653      * Configures the padding for the target zone in px.  Effectively expands
654      * (or reduces) the virtual object size for targeting calculations.
655      * Supports css-style shorthand; if only one parameter is passed, all sides
656      * will have that padding, and if only two are passed, the top and bottom
657      * will have the first param, the left and right the second.
658      * @method setPadding
659      * @param {int} iTop    Top pad
660      * @param {int} iRight  Right pad
661      * @param {int} iBot    Bot pad
662      * @param {int} iLeft   Left pad
663      */
664     setPadding: function(iTop, iRight, iBot, iLeft) {
665         // this.padding = [iLeft, iRight, iTop, iBot];
666         if (!iRight && 0 !== iRight) {
667             this.padding = [iTop, iTop, iTop, iTop];
668         } else if (!iBot && 0 !== iBot) {
669             this.padding = [iTop, iRight, iTop, iRight];
670         } else {
671             this.padding = [iTop, iRight, iBot, iLeft];
672         }
673     },
674
675     /**
676      * Stores the initial placement of the linked element.
677      * @method setInitialPosition
678      * @param {int} diffX   the X offset, default 0
679      * @param {int} diffY   the Y offset, default 0
680      */
681     setInitPosition: function(diffX, diffY) {
682         var el = this.getEl();
683
684         if (!this.DDM.verifyEl(el)) {
685             return;
686         }
687
688         var dx = diffX || 0;
689         var dy = diffY || 0;
690
691         var p = Dom.getXY( el );
692
693         this.initPageX = p[0] - dx;
694         this.initPageY = p[1] - dy;
695
696         this.lastPageX = p[0];
697         this.lastPageY = p[1];
698
699
700         this.setStartPosition(p);
701     },
702
703     /**
704      * Sets the start position of the element.  This is set when the obj
705      * is initialized, the reset when a drag is started.
706      * @method setStartPosition
707      * @param pos current position (from previous lookup)
708      * @private
709      */
710     setStartPosition: function(pos) {
711         var p = pos || Dom.getXY( this.getEl() );
712         this.deltaSetXY = null;
713
714         this.startPageX = p[0];
715         this.startPageY = p[1];
716     },
717
718     /**
719      * Add this instance to a group of related drag/drop objects.  All
720      * instances belong to at least one group, and can belong to as many
721      * groups as needed.
722      * @method addToGroup
723      * @param sGroup {string} the name of the group
724      */
725     addToGroup: function(sGroup) {
726         this.groups[sGroup] = true;
727         this.DDM.regDragDrop(this, sGroup);
728     },
729
730     /**
731      * Remove's this instance from the supplied interaction group
732      * @method removeFromGroup
733      * @param {string}  sGroup  The group to drop
734      */
735     removeFromGroup: function(sGroup) {
736         if (this.groups[sGroup]) {
737             delete this.groups[sGroup];
738         }
739
740         this.DDM.removeDDFromGroup(this, sGroup);
741     },
742
743     /**
744      * Allows you to specify that an element other than the linked element
745      * will be moved with the cursor during a drag
746      * @method setDragElId
747      * @param id {string} the id of the element that will be used to initiate the drag
748      */
749     setDragElId: function(id) {
750         this.dragElId = id;
751     },
752
753     /**
754      * Allows you to specify a child of the linked element that should be
755      * used to initiate the drag operation.  An example of this would be if
756      * you have a content div with text and links.  Clicking anywhere in the
757      * content area would normally start the drag operation.  Use this method
758      * to specify that an element inside of the content div is the element
759      * that starts the drag operation.
760      * @method setHandleElId
761      * @param id {string} the id of the element that will be used to
762      * initiate the drag.
763      */
764     setHandleElId: function(id) {
765         if (typeof id !== "string") {
766             id = Roo.id(id);
767         }
768         this.handleElId = id;
769         this.DDM.regHandle(this.id, id);
770     },
771
772     /**
773      * Allows you to set an element outside of the linked element as a drag
774      * handle
775      * @method setOuterHandleElId
776      * @param id the id of the element that will be used to initiate the drag
777      */
778     setOuterHandleElId: function(id) {
779         if (typeof id !== "string") {
780             id = Roo.id(id);
781         }
782         Event.on(id, "mousedown",
783                 this.handleMouseDown, this);
784         this.setHandleElId(id);
785
786         this.hasOuterHandles = true;
787     },
788
789     /**
790      * Remove all drag and drop hooks for this element
791      * @method unreg
792      */
793     unreg: function() {
794         Event.un(this.id, "mousedown",
795                 this.handleMouseDown);
796         Event.un(this.id, "touchstart",
797                 this.handleMouseDown);
798         this._domRef = null;
799         this.DDM._remove(this);
800     },
801
802     destroy : function(){
803         this.unreg();
804     },
805
806     /**
807      * Returns true if this instance is locked, or the drag drop mgr is locked
808      * (meaning that all drag/drop is disabled on the page.)
809      * @method isLocked
810      * @return {boolean} true if this obj or all drag/drop is locked, else
811      * false
812      */
813     isLocked: function() {
814         return (this.DDM.isLocked() || this.locked);
815     },
816
817     /**
818      * Fired when this object is clicked
819      * @method handleMouseDown
820      * @param {Event} e
821      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
822      * @private
823      */
824     handleMouseDown: function(e, oDD){
825      
826         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
827             //Roo.log('not touch/ button !=0');
828             return;
829         }
830         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
831             return; // double touch..
832         }
833         
834
835         if (this.isLocked()) {
836             //Roo.log('locked');
837             return;
838         }
839
840         this.DDM.refreshCache(this.groups);
841 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
842         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
843         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
844             //Roo.log('no outer handes or not over target');
845                 // do nothing.
846         } else {
847 //            Roo.log('check validator');
848             if (this.clickValidator(e)) {
849 //                Roo.log('validate success');
850                 // set the initial element position
851                 this.setStartPosition();
852
853
854                 this.b4MouseDown(e);
855                 this.onMouseDown(e);
856
857                 this.DDM.handleMouseDown(e, this);
858
859                 this.DDM.stopEvent(e);
860             } else {
861
862
863             }
864         }
865     },
866
867     clickValidator: function(e) {
868         var target = e.getTarget();
869         return ( this.isValidHandleChild(target) &&
870                     (this.id == this.handleElId ||
871                         this.DDM.handleWasClicked(target, this.id)) );
872     },
873
874     /**
875      * Allows you to specify a tag name that should not start a drag operation
876      * when clicked.  This is designed to facilitate embedding links within a
877      * drag handle that do something other than start the drag.
878      * @method addInvalidHandleType
879      * @param {string} tagName the type of element to exclude
880      */
881     addInvalidHandleType: function(tagName) {
882         var type = tagName.toUpperCase();
883         this.invalidHandleTypes[type] = type;
884     },
885
886     /**
887      * Lets you to specify an element id for a child of a drag handle
888      * that should not initiate a drag
889      * @method addInvalidHandleId
890      * @param {string} id the element id of the element you wish to ignore
891      */
892     addInvalidHandleId: function(id) {
893         if (typeof id !== "string") {
894             id = Roo.id(id);
895         }
896         this.invalidHandleIds[id] = id;
897     },
898
899     /**
900      * Lets you specify a css class of elements that will not initiate a drag
901      * @method addInvalidHandleClass
902      * @param {string} cssClass the class of the elements you wish to ignore
903      */
904     addInvalidHandleClass: function(cssClass) {
905         this.invalidHandleClasses.push(cssClass);
906     },
907
908     /**
909      * Unsets an excluded tag name set by addInvalidHandleType
910      * @method removeInvalidHandleType
911      * @param {string} tagName the type of element to unexclude
912      */
913     removeInvalidHandleType: function(tagName) {
914         var type = tagName.toUpperCase();
915         // this.invalidHandleTypes[type] = null;
916         delete this.invalidHandleTypes[type];
917     },
918
919     /**
920      * Unsets an invalid handle id
921      * @method removeInvalidHandleId
922      * @param {string} id the id of the element to re-enable
923      */
924     removeInvalidHandleId: function(id) {
925         if (typeof id !== "string") {
926             id = Roo.id(id);
927         }
928         delete this.invalidHandleIds[id];
929     },
930
931     /**
932      * Unsets an invalid css class
933      * @method removeInvalidHandleClass
934      * @param {string} cssClass the class of the element(s) you wish to
935      * re-enable
936      */
937     removeInvalidHandleClass: function(cssClass) {
938         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
939             if (this.invalidHandleClasses[i] == cssClass) {
940                 delete this.invalidHandleClasses[i];
941             }
942         }
943     },
944
945     /**
946      * Checks the tag exclusion list to see if this click should be ignored
947      * @method isValidHandleChild
948      * @param {HTMLElement} node the HTMLElement to evaluate
949      * @return {boolean} true if this is a valid tag type, false if not
950      */
951     isValidHandleChild: function(node) {
952
953         var valid = true;
954         // var n = (node.nodeName == "#text") ? node.parentNode : node;
955         var nodeName;
956         try {
957             nodeName = node.nodeName.toUpperCase();
958         } catch(e) {
959             nodeName = node.nodeName;
960         }
961         valid = valid && !this.invalidHandleTypes[nodeName];
962         valid = valid && !this.invalidHandleIds[node.id];
963
964         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
965             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
966         }
967
968
969         return valid;
970
971     },
972
973     /**
974      * Create the array of horizontal tick marks if an interval was specified
975      * in setXConstraint().
976      * @method setXTicks
977      * @private
978      */
979     setXTicks: function(iStartX, iTickSize) {
980         this.xTicks = [];
981         this.xTickSize = iTickSize;
982
983         var tickMap = {};
984
985         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
986             if (!tickMap[i]) {
987                 this.xTicks[this.xTicks.length] = i;
988                 tickMap[i] = true;
989             }
990         }
991
992         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
993             if (!tickMap[i]) {
994                 this.xTicks[this.xTicks.length] = i;
995                 tickMap[i] = true;
996             }
997         }
998
999         this.xTicks.sort(this.DDM.numericSort) ;
1000     },
1001
1002     /**
1003      * Create the array of vertical tick marks if an interval was specified in
1004      * setYConstraint().
1005      * @method setYTicks
1006      * @private
1007      */
1008     setYTicks: function(iStartY, iTickSize) {
1009         this.yTicks = [];
1010         this.yTickSize = iTickSize;
1011
1012         var tickMap = {};
1013
1014         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1015             if (!tickMap[i]) {
1016                 this.yTicks[this.yTicks.length] = i;
1017                 tickMap[i] = true;
1018             }
1019         }
1020
1021         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1022             if (!tickMap[i]) {
1023                 this.yTicks[this.yTicks.length] = i;
1024                 tickMap[i] = true;
1025             }
1026         }
1027
1028         this.yTicks.sort(this.DDM.numericSort) ;
1029     },
1030
1031     /**
1032      * By default, the element can be dragged any place on the screen.  Use
1033      * this method to limit the horizontal travel of the element.  Pass in
1034      * 0,0 for the parameters if you want to lock the drag to the y axis.
1035      * @method setXConstraint
1036      * @param {int} iLeft the number of pixels the element can move to the left
1037      * @param {int} iRight the number of pixels the element can move to the
1038      * right
1039      * @param {int} iTickSize optional parameter for specifying that the
1040      * element
1041      * should move iTickSize pixels at a time.
1042      */
1043     setXConstraint: function(iLeft, iRight, iTickSize) {
1044         this.leftConstraint = iLeft;
1045         this.rightConstraint = iRight;
1046
1047         this.minX = this.initPageX - iLeft;
1048         this.maxX = this.initPageX + iRight;
1049         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1050
1051         this.constrainX = true;
1052     },
1053
1054     /**
1055      * Clears any constraints applied to this instance.  Also clears ticks
1056      * since they can't exist independent of a constraint at this time.
1057      * @method clearConstraints
1058      */
1059     clearConstraints: function() {
1060         this.constrainX = false;
1061         this.constrainY = false;
1062         this.clearTicks();
1063     },
1064
1065     /**
1066      * Clears any tick interval defined for this instance
1067      * @method clearTicks
1068      */
1069     clearTicks: function() {
1070         this.xTicks = null;
1071         this.yTicks = null;
1072         this.xTickSize = 0;
1073         this.yTickSize = 0;
1074     },
1075
1076     /**
1077      * By default, the element can be dragged any place on the screen.  Set
1078      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1079      * parameters if you want to lock the drag to the x axis.
1080      * @method setYConstraint
1081      * @param {int} iUp the number of pixels the element can move up
1082      * @param {int} iDown the number of pixels the element can move down
1083      * @param {int} iTickSize optional parameter for specifying that the
1084      * element should move iTickSize pixels at a time.
1085      */
1086     setYConstraint: function(iUp, iDown, iTickSize) {
1087         this.topConstraint = iUp;
1088         this.bottomConstraint = iDown;
1089
1090         this.minY = this.initPageY - iUp;
1091         this.maxY = this.initPageY + iDown;
1092         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1093
1094         this.constrainY = true;
1095
1096     },
1097
1098     /**
1099      * resetConstraints must be called if you manually reposition a dd element.
1100      * @method resetConstraints
1101      * @param {boolean} maintainOffset
1102      */
1103     resetConstraints: function() {
1104
1105
1106         // Maintain offsets if necessary
1107         if (this.initPageX || this.initPageX === 0) {
1108             // figure out how much this thing has moved
1109             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1110             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1111
1112             this.setInitPosition(dx, dy);
1113
1114         // This is the first time we have detected the element's position
1115         } else {
1116             this.setInitPosition();
1117         }
1118
1119         if (this.constrainX) {
1120             this.setXConstraint( this.leftConstraint,
1121                                  this.rightConstraint,
1122                                  this.xTickSize        );
1123         }
1124
1125         if (this.constrainY) {
1126             this.setYConstraint( this.topConstraint,
1127                                  this.bottomConstraint,
1128                                  this.yTickSize         );
1129         }
1130     },
1131
1132     /**
1133      * Normally the drag element is moved pixel by pixel, but we can specify
1134      * that it move a number of pixels at a time.  This method resolves the
1135      * location when we have it set up like this.
1136      * @method getTick
1137      * @param {int} val where we want to place the object
1138      * @param {int[]} tickArray sorted array of valid points
1139      * @return {int} the closest tick
1140      * @private
1141      */
1142     getTick: function(val, tickArray) {
1143
1144         if (!tickArray) {
1145             // If tick interval is not defined, it is effectively 1 pixel,
1146             // so we return the value passed to us.
1147             return val;
1148         } else if (tickArray[0] >= val) {
1149             // The value is lower than the first tick, so we return the first
1150             // tick.
1151             return tickArray[0];
1152         } else {
1153             for (var i=0, len=tickArray.length; i<len; ++i) {
1154                 var next = i + 1;
1155                 if (tickArray[next] && tickArray[next] >= val) {
1156                     var diff1 = val - tickArray[i];
1157                     var diff2 = tickArray[next] - val;
1158                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1159                 }
1160             }
1161
1162             // The value is larger than the last tick, so we return the last
1163             // tick.
1164             return tickArray[tickArray.length - 1];
1165         }
1166     },
1167
1168     /**
1169      * toString method
1170      * @method toString
1171      * @return {string} string representation of the dd obj
1172      */
1173     toString: function() {
1174         return ("DragDrop " + this.id);
1175     }
1176
1177 });
1178
1179 })();
1180 /*
1181  * Based on:
1182  * Ext JS Library 1.1.1
1183  * Copyright(c) 2006-2007, Ext JS, LLC.
1184  *
1185  * Originally Released Under LGPL - original licence link has changed is not relivant.
1186  *
1187  * Fork - LGPL
1188  * <script type="text/javascript">
1189  */
1190
1191
1192 /**
1193  * The drag and drop utility provides a framework for building drag and drop
1194  * applications.  In addition to enabling drag and drop for specific elements,
1195  * the drag and drop elements are tracked by the manager class, and the
1196  * interactions between the various elements are tracked during the drag and
1197  * the implementing code is notified about these important moments.
1198  */
1199
1200 // Only load the library once.  Rewriting the manager class would orphan
1201 // existing drag and drop instances.
1202 if (!Roo.dd.DragDropMgr) {
1203
1204 /**
1205  * @class Roo.dd.DragDropMgr
1206  * DragDropMgr is a singleton that tracks the element interaction for
1207  * all DragDrop items in the window.  Generally, you will not call
1208  * this class directly, but it does have helper methods that could
1209  * be useful in your DragDrop implementations.
1210  * @singleton
1211  */
1212 Roo.dd.DragDropMgr = function() {
1213
1214     var Event = Roo.EventManager;
1215
1216     return {
1217
1218         /**
1219          * Two dimensional Array of registered DragDrop objects.  The first
1220          * dimension is the DragDrop item group, the second the DragDrop
1221          * object.
1222          * @property ids
1223          * @type {string: string}
1224          * @private
1225          * @static
1226          */
1227         ids: {},
1228
1229         /**
1230          * Array of element ids defined as drag handles.  Used to determine
1231          * if the element that generated the mousedown event is actually the
1232          * handle and not the html element itself.
1233          * @property handleIds
1234          * @type {string: string}
1235          * @private
1236          * @static
1237          */
1238         handleIds: {},
1239
1240         /**
1241          * the DragDrop object that is currently being dragged
1242          * @property dragCurrent
1243          * @type DragDrop
1244          * @private
1245          * @static
1246          **/
1247         dragCurrent: null,
1248
1249         /**
1250          * the DragDrop object(s) that are being hovered over
1251          * @property dragOvers
1252          * @type Array
1253          * @private
1254          * @static
1255          */
1256         dragOvers: {},
1257
1258         /**
1259          * the X distance between the cursor and the object being dragged
1260          * @property deltaX
1261          * @type int
1262          * @private
1263          * @static
1264          */
1265         deltaX: 0,
1266
1267         /**
1268          * the Y distance between the cursor and the object being dragged
1269          * @property deltaY
1270          * @type int
1271          * @private
1272          * @static
1273          */
1274         deltaY: 0,
1275
1276         /**
1277          * Flag to determine if we should prevent the default behavior of the
1278          * events we define. By default this is true, but this can be set to
1279          * false if you need the default behavior (not recommended)
1280          * @property preventDefault
1281          * @type boolean
1282          * @static
1283          */
1284         preventDefault: true,
1285
1286         /**
1287          * Flag to determine if we should stop the propagation of the events
1288          * we generate. This is true by default but you may want to set it to
1289          * false if the html element contains other features that require the
1290          * mouse click.
1291          * @property stopPropagation
1292          * @type boolean
1293          * @static
1294          */
1295         stopPropagation: true,
1296
1297         /**
1298          * Internal flag that is set to true when drag and drop has been
1299          * intialized
1300          * @property initialized
1301          * @private
1302          * @static
1303          */
1304         initalized: false,
1305
1306         /**
1307          * All drag and drop can be disabled.
1308          * @property locked
1309          * @private
1310          * @static
1311          */
1312         locked: false,
1313
1314         /**
1315          * Called the first time an element is registered.
1316          * @method init
1317          * @private
1318          * @static
1319          */
1320         init: function() {
1321             this.initialized = true;
1322         },
1323
1324         /**
1325          * In point mode, drag and drop interaction is defined by the
1326          * location of the cursor during the drag/drop
1327          * @property POINT
1328          * @type int
1329          * @static
1330          */
1331         POINT: 0,
1332
1333         /**
1334          * In intersect mode, drag and drop interactio nis defined by the
1335          * overlap of two or more drag and drop objects.
1336          * @property INTERSECT
1337          * @type int
1338          * @static
1339          */
1340         INTERSECT: 1,
1341
1342         /**
1343          * The current drag and drop mode.  Default: POINT
1344          * @property mode
1345          * @type int
1346          * @static
1347          */
1348         mode: 0,
1349
1350         /**
1351          * Runs method on all drag and drop objects
1352          * @method _execOnAll
1353          * @private
1354          * @static
1355          */
1356         _execOnAll: function(sMethod, args) {
1357             for (var i in this.ids) {
1358                 for (var j in this.ids[i]) {
1359                     var oDD = this.ids[i][j];
1360                     if (! this.isTypeOfDD(oDD)) {
1361                         continue;
1362                     }
1363                     oDD[sMethod].apply(oDD, args);
1364                 }
1365             }
1366         },
1367
1368         /**
1369          * Drag and drop initialization.  Sets up the global event handlers
1370          * @method _onLoad
1371          * @private
1372          * @static
1373          */
1374         _onLoad: function() {
1375
1376             this.init();
1377
1378             if (!Roo.isTouch) {
1379                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1380                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
1381             }
1382             Event.on(document, "touchend",   this.handleMouseUp, this, true);
1383             Event.on(document, "touchmove", this.handleMouseMove, this, true);
1384             
1385             Event.on(window,   "unload",    this._onUnload, this, true);
1386             Event.on(window,   "resize",    this._onResize, this, true);
1387             // Event.on(window,   "mouseout",    this._test);
1388
1389         },
1390
1391         /**
1392          * Reset constraints on all drag and drop objs
1393          * @method _onResize
1394          * @private
1395          * @static
1396          */
1397         _onResize: function(e) {
1398             this._execOnAll("resetConstraints", []);
1399         },
1400
1401         /**
1402          * Lock all drag and drop functionality
1403          * @method lock
1404          * @static
1405          */
1406         lock: function() { this.locked = true; },
1407
1408         /**
1409          * Unlock all drag and drop functionality
1410          * @method unlock
1411          * @static
1412          */
1413         unlock: function() { this.locked = false; },
1414
1415         /**
1416          * Is drag and drop locked?
1417          * @method isLocked
1418          * @return {boolean} True if drag and drop is locked, false otherwise.
1419          * @static
1420          */
1421         isLocked: function() { return this.locked; },
1422
1423         /**
1424          * Location cache that is set for all drag drop objects when a drag is
1425          * initiated, cleared when the drag is finished.
1426          * @property locationCache
1427          * @private
1428          * @static
1429          */
1430         locationCache: {},
1431
1432         /**
1433          * Set useCache to false if you want to force object the lookup of each
1434          * drag and drop linked element constantly during a drag.
1435          * @property useCache
1436          * @type boolean
1437          * @static
1438          */
1439         useCache: true,
1440
1441         /**
1442          * The number of pixels that the mouse needs to move after the
1443          * mousedown before the drag is initiated.  Default=3;
1444          * @property clickPixelThresh
1445          * @type int
1446          * @static
1447          */
1448         clickPixelThresh: 3,
1449
1450         /**
1451          * The number of milliseconds after the mousedown event to initiate the
1452          * drag if we don't get a mouseup event. Default=1000
1453          * @property clickTimeThresh
1454          * @type int
1455          * @static
1456          */
1457         clickTimeThresh: 350,
1458
1459         /**
1460          * Flag that indicates that either the drag pixel threshold or the
1461          * mousdown time threshold has been met
1462          * @property dragThreshMet
1463          * @type boolean
1464          * @private
1465          * @static
1466          */
1467         dragThreshMet: false,
1468
1469         /**
1470          * Timeout used for the click time threshold
1471          * @property clickTimeout
1472          * @type Object
1473          * @private
1474          * @static
1475          */
1476         clickTimeout: null,
1477
1478         /**
1479          * The X position of the mousedown event stored for later use when a
1480          * drag threshold is met.
1481          * @property startX
1482          * @type int
1483          * @private
1484          * @static
1485          */
1486         startX: 0,
1487
1488         /**
1489          * The Y position of the mousedown event stored for later use when a
1490          * drag threshold is met.
1491          * @property startY
1492          * @type int
1493          * @private
1494          * @static
1495          */
1496         startY: 0,
1497
1498         /**
1499          * Each DragDrop instance must be registered with the DragDropMgr.
1500          * This is executed in DragDrop.init()
1501          * @method regDragDrop
1502          * @param {DragDrop} oDD the DragDrop object to register
1503          * @param {String} sGroup the name of the group this element belongs to
1504          * @static
1505          */
1506         regDragDrop: function(oDD, sGroup) {
1507             if (!this.initialized) { this.init(); }
1508
1509             if (!this.ids[sGroup]) {
1510                 this.ids[sGroup] = {};
1511             }
1512             this.ids[sGroup][oDD.id] = oDD;
1513         },
1514
1515         /**
1516          * Removes the supplied dd instance from the supplied group. Executed
1517          * by DragDrop.removeFromGroup, so don't call this function directly.
1518          * @method removeDDFromGroup
1519          * @private
1520          * @static
1521          */
1522         removeDDFromGroup: function(oDD, sGroup) {
1523             if (!this.ids[sGroup]) {
1524                 this.ids[sGroup] = {};
1525             }
1526
1527             var obj = this.ids[sGroup];
1528             if (obj && obj[oDD.id]) {
1529                 delete obj[oDD.id];
1530             }
1531         },
1532
1533         /**
1534          * Unregisters a drag and drop item.  This is executed in
1535          * DragDrop.unreg, use that method instead of calling this directly.
1536          * @method _remove
1537          * @private
1538          * @static
1539          */
1540         _remove: function(oDD) {
1541             for (var g in oDD.groups) {
1542                 if (g && this.ids[g][oDD.id]) {
1543                     delete this.ids[g][oDD.id];
1544                 }
1545             }
1546             delete this.handleIds[oDD.id];
1547         },
1548
1549         /**
1550          * Each DragDrop handle element must be registered.  This is done
1551          * automatically when executing DragDrop.setHandleElId()
1552          * @method regHandle
1553          * @param {String} sDDId the DragDrop id this element is a handle for
1554          * @param {String} sHandleId the id of the element that is the drag
1555          * handle
1556          * @static
1557          */
1558         regHandle: function(sDDId, sHandleId) {
1559             if (!this.handleIds[sDDId]) {
1560                 this.handleIds[sDDId] = {};
1561             }
1562             this.handleIds[sDDId][sHandleId] = sHandleId;
1563         },
1564
1565         /**
1566          * Utility function to determine if a given element has been
1567          * registered as a drag drop item.
1568          * @method isDragDrop
1569          * @param {String} id the element id to check
1570          * @return {boolean} true if this element is a DragDrop item,
1571          * false otherwise
1572          * @static
1573          */
1574         isDragDrop: function(id) {
1575             return ( this.getDDById(id) ) ? true : false;
1576         },
1577
1578         /**
1579          * Returns the drag and drop instances that are in all groups the
1580          * passed in instance belongs to.
1581          * @method getRelated
1582          * @param {DragDrop} p_oDD the obj to get related data for
1583          * @param {boolean} bTargetsOnly if true, only return targetable objs
1584          * @return {DragDrop[]} the related instances
1585          * @static
1586          */
1587         getRelated: function(p_oDD, bTargetsOnly) {
1588             var oDDs = [];
1589             for (var i in p_oDD.groups) {
1590                 for (j in this.ids[i]) {
1591                     var dd = this.ids[i][j];
1592                     if (! this.isTypeOfDD(dd)) {
1593                         continue;
1594                     }
1595                     if (!bTargetsOnly || dd.isTarget) {
1596                         oDDs[oDDs.length] = dd;
1597                     }
1598                 }
1599             }
1600
1601             return oDDs;
1602         },
1603
1604         /**
1605          * Returns true if the specified dd target is a legal target for
1606          * the specifice drag obj
1607          * @method isLegalTarget
1608          * @param {DragDrop} the drag obj
1609          * @param {DragDrop} the target
1610          * @return {boolean} true if the target is a legal target for the
1611          * dd obj
1612          * @static
1613          */
1614         isLegalTarget: function (oDD, oTargetDD) {
1615             var targets = this.getRelated(oDD, true);
1616             for (var i=0, len=targets.length;i<len;++i) {
1617                 if (targets[i].id == oTargetDD.id) {
1618                     return true;
1619                 }
1620             }
1621
1622             return false;
1623         },
1624
1625         /**
1626          * My goal is to be able to transparently determine if an object is
1627          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1628          * returns "object", oDD.constructor.toString() always returns
1629          * "DragDrop" and not the name of the subclass.  So for now it just
1630          * evaluates a well-known variable in DragDrop.
1631          * @method isTypeOfDD
1632          * @param {Object} the object to evaluate
1633          * @return {boolean} true if typeof oDD = DragDrop
1634          * @static
1635          */
1636         isTypeOfDD: function (oDD) {
1637             return (oDD && oDD.__ygDragDrop);
1638         },
1639
1640         /**
1641          * Utility function to determine if a given element has been
1642          * registered as a drag drop handle for the given Drag Drop object.
1643          * @method isHandle
1644          * @param {String} id the element id to check
1645          * @return {boolean} true if this element is a DragDrop handle, false
1646          * otherwise
1647          * @static
1648          */
1649         isHandle: function(sDDId, sHandleId) {
1650             return ( this.handleIds[sDDId] &&
1651                             this.handleIds[sDDId][sHandleId] );
1652         },
1653
1654         /**
1655          * Returns the DragDrop instance for a given id
1656          * @method getDDById
1657          * @param {String} id the id of the DragDrop object
1658          * @return {DragDrop} the drag drop object, null if it is not found
1659          * @static
1660          */
1661         getDDById: function(id) {
1662             for (var i in this.ids) {
1663                 if (this.ids[i][id]) {
1664                     return this.ids[i][id];
1665                 }
1666             }
1667             return null;
1668         },
1669
1670         /**
1671          * Fired after a registered DragDrop object gets the mousedown event.
1672          * Sets up the events required to track the object being dragged
1673          * @method handleMouseDown
1674          * @param {Event} e the event
1675          * @param oDD the DragDrop object being dragged
1676          * @private
1677          * @static
1678          */
1679         handleMouseDown: function(e, oDD) {
1680             if(Roo.QuickTips){
1681                 Roo.QuickTips.disable();
1682             }
1683             this.currentTarget = e.getTarget();
1684
1685             this.dragCurrent = oDD;
1686
1687             var el = oDD.getEl();
1688
1689             // track start position
1690             this.startX = e.getPageX();
1691             this.startY = e.getPageY();
1692
1693             this.deltaX = this.startX - el.offsetLeft;
1694             this.deltaY = this.startY - el.offsetTop;
1695
1696             this.dragThreshMet = false;
1697
1698             this.clickTimeout = setTimeout(
1699                     function() {
1700                         var DDM = Roo.dd.DDM;
1701                         DDM.startDrag(DDM.startX, DDM.startY);
1702                     },
1703                     this.clickTimeThresh );
1704         },
1705
1706         /**
1707          * Fired when either the drag pixel threshol or the mousedown hold
1708          * time threshold has been met.
1709          * @method startDrag
1710          * @param x {int} the X position of the original mousedown
1711          * @param y {int} the Y position of the original mousedown
1712          * @static
1713          */
1714         startDrag: function(x, y) {
1715             clearTimeout(this.clickTimeout);
1716             if (this.dragCurrent) {
1717                 this.dragCurrent.b4StartDrag(x, y);
1718                 this.dragCurrent.startDrag(x, y);
1719             }
1720             this.dragThreshMet = true;
1721         },
1722
1723         /**
1724          * Internal function to handle the mouseup event.  Will be invoked
1725          * from the context of the document.
1726          * @method handleMouseUp
1727          * @param {Event} e the event
1728          * @private
1729          * @static
1730          */
1731         handleMouseUp: function(e) {
1732
1733             if(Roo.QuickTips){
1734                 Roo.QuickTips.enable();
1735             }
1736             if (! this.dragCurrent) {
1737                 return;
1738             }
1739
1740             clearTimeout(this.clickTimeout);
1741
1742             if (this.dragThreshMet) {
1743                 this.fireEvents(e, true);
1744             } else {
1745             }
1746
1747             this.stopDrag(e);
1748
1749             this.stopEvent(e);
1750         },
1751
1752         /**
1753          * Utility to stop event propagation and event default, if these
1754          * features are turned on.
1755          * @method stopEvent
1756          * @param {Event} e the event as returned by this.getEvent()
1757          * @static
1758          */
1759         stopEvent: function(e){
1760             if(this.stopPropagation) {
1761                 e.stopPropagation();
1762             }
1763
1764             if (this.preventDefault) {
1765                 e.preventDefault();
1766             }
1767         },
1768
1769         /**
1770          * Internal function to clean up event handlers after the drag
1771          * operation is complete
1772          * @method stopDrag
1773          * @param {Event} e the event
1774          * @private
1775          * @static
1776          */
1777         stopDrag: function(e) {
1778             // Fire the drag end event for the item that was dragged
1779             if (this.dragCurrent) {
1780                 if (this.dragThreshMet) {
1781                     this.dragCurrent.b4EndDrag(e);
1782                     this.dragCurrent.endDrag(e);
1783                 }
1784
1785                 this.dragCurrent.onMouseUp(e);
1786             }
1787
1788             this.dragCurrent = null;
1789             this.dragOvers = {};
1790         },
1791
1792         /**
1793          * Internal function to handle the mousemove event.  Will be invoked
1794          * from the context of the html element.
1795          *
1796          * @TODO figure out what we can do about mouse events lost when the
1797          * user drags objects beyond the window boundary.  Currently we can
1798          * detect this in internet explorer by verifying that the mouse is
1799          * down during the mousemove event.  Firefox doesn't give us the
1800          * button state on the mousemove event.
1801          * @method handleMouseMove
1802          * @param {Event} e the event
1803          * @private
1804          * @static
1805          */
1806         handleMouseMove: function(e) {
1807             if (! this.dragCurrent) {
1808                 return true;
1809             }
1810
1811             // var button = e.which || e.button;
1812
1813             // check for IE mouseup outside of page boundary
1814             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1815                 this.stopEvent(e);
1816                 return this.handleMouseUp(e);
1817             }
1818
1819             if (!this.dragThreshMet) {
1820                 var diffX = Math.abs(this.startX - e.getPageX());
1821                 var diffY = Math.abs(this.startY - e.getPageY());
1822                 if (diffX > this.clickPixelThresh ||
1823                             diffY > this.clickPixelThresh) {
1824                     this.startDrag(this.startX, this.startY);
1825                 }
1826             }
1827
1828             if (this.dragThreshMet) {
1829                 this.dragCurrent.b4Drag(e);
1830                 this.dragCurrent.onDrag(e);
1831                 if(!this.dragCurrent.moveOnly){
1832                     this.fireEvents(e, false);
1833                 }
1834             }
1835
1836             this.stopEvent(e);
1837
1838             return true;
1839         },
1840
1841         /**
1842          * Iterates over all of the DragDrop elements to find ones we are
1843          * hovering over or dropping on
1844          * @method fireEvents
1845          * @param {Event} e the event
1846          * @param {boolean} isDrop is this a drop op or a mouseover op?
1847          * @private
1848          * @static
1849          */
1850         fireEvents: function(e, isDrop) {
1851             var dc = this.dragCurrent;
1852
1853             // If the user did the mouse up outside of the window, we could
1854             // get here even though we have ended the drag.
1855             if (!dc || dc.isLocked()) {
1856                 return;
1857             }
1858
1859             var pt = e.getPoint();
1860
1861             // cache the previous dragOver array
1862             var oldOvers = [];
1863
1864             var outEvts   = [];
1865             var overEvts  = [];
1866             var dropEvts  = [];
1867             var enterEvts = [];
1868
1869             // Check to see if the object(s) we were hovering over is no longer
1870             // being hovered over so we can fire the onDragOut event
1871             for (var i in this.dragOvers) {
1872
1873                 var ddo = this.dragOvers[i];
1874
1875                 if (! this.isTypeOfDD(ddo)) {
1876                     continue;
1877                 }
1878
1879                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1880                     outEvts.push( ddo );
1881                 }
1882
1883                 oldOvers[i] = true;
1884                 delete this.dragOvers[i];
1885             }
1886
1887             for (var sGroup in dc.groups) {
1888
1889                 if ("string" != typeof sGroup) {
1890                     continue;
1891                 }
1892
1893                 for (i in this.ids[sGroup]) {
1894                     var oDD = this.ids[sGroup][i];
1895                     if (! this.isTypeOfDD(oDD)) {
1896                         continue;
1897                     }
1898
1899                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1900                         if (this.isOverTarget(pt, oDD, this.mode)) {
1901                             // look for drop interactions
1902                             if (isDrop) {
1903                                 dropEvts.push( oDD );
1904                             // look for drag enter and drag over interactions
1905                             } else {
1906
1907                                 // initial drag over: dragEnter fires
1908                                 if (!oldOvers[oDD.id]) {
1909                                     enterEvts.push( oDD );
1910                                 // subsequent drag overs: dragOver fires
1911                                 } else {
1912                                     overEvts.push( oDD );
1913                                 }
1914
1915                                 this.dragOvers[oDD.id] = oDD;
1916                             }
1917                         }
1918                     }
1919                 }
1920             }
1921
1922             if (this.mode) {
1923                 if (outEvts.length) {
1924                     dc.b4DragOut(e, outEvts);
1925                     dc.onDragOut(e, outEvts);
1926                 }
1927
1928                 if (enterEvts.length) {
1929                     dc.onDragEnter(e, enterEvts);
1930                 }
1931
1932                 if (overEvts.length) {
1933                     dc.b4DragOver(e, overEvts);
1934                     dc.onDragOver(e, overEvts);
1935                 }
1936
1937                 if (dropEvts.length) {
1938                     dc.b4DragDrop(e, dropEvts);
1939                     dc.onDragDrop(e, dropEvts);
1940                 }
1941
1942             } else {
1943                 // fire dragout events
1944                 var len = 0;
1945                 for (i=0, len=outEvts.length; i<len; ++i) {
1946                     dc.b4DragOut(e, outEvts[i].id);
1947                     dc.onDragOut(e, outEvts[i].id);
1948                 }
1949
1950                 // fire enter events
1951                 for (i=0,len=enterEvts.length; i<len; ++i) {
1952                     // dc.b4DragEnter(e, oDD.id);
1953                     dc.onDragEnter(e, enterEvts[i].id);
1954                 }
1955
1956                 // fire over events
1957                 for (i=0,len=overEvts.length; i<len; ++i) {
1958                     dc.b4DragOver(e, overEvts[i].id);
1959                     dc.onDragOver(e, overEvts[i].id);
1960                 }
1961
1962                 // fire drop events
1963                 for (i=0, len=dropEvts.length; i<len; ++i) {
1964                     dc.b4DragDrop(e, dropEvts[i].id);
1965                     dc.onDragDrop(e, dropEvts[i].id);
1966                 }
1967
1968             }
1969
1970             // notify about a drop that did not find a target
1971             if (isDrop && !dropEvts.length) {
1972                 dc.onInvalidDrop(e);
1973             }
1974
1975         },
1976
1977         /**
1978          * Helper function for getting the best match from the list of drag
1979          * and drop objects returned by the drag and drop events when we are
1980          * in INTERSECT mode.  It returns either the first object that the
1981          * cursor is over, or the object that has the greatest overlap with
1982          * the dragged element.
1983          * @method getBestMatch
1984          * @param  {DragDrop[]} dds The array of drag and drop objects
1985          * targeted
1986          * @return {DragDrop}       The best single match
1987          * @static
1988          */
1989         getBestMatch: function(dds) {
1990             var winner = null;
1991             // Return null if the input is not what we expect
1992             //if (!dds || !dds.length || dds.length == 0) {
1993                // winner = null;
1994             // If there is only one item, it wins
1995             //} else if (dds.length == 1) {
1996
1997             var len = dds.length;
1998
1999             if (len == 1) {
2000                 winner = dds[0];
2001             } else {
2002                 // Loop through the targeted items
2003                 for (var i=0; i<len; ++i) {
2004                     var dd = dds[i];
2005                     // If the cursor is over the object, it wins.  If the
2006                     // cursor is over multiple matches, the first one we come
2007                     // to wins.
2008                     if (dd.cursorIsOver) {
2009                         winner = dd;
2010                         break;
2011                     // Otherwise the object with the most overlap wins
2012                     } else {
2013                         if (!winner ||
2014                             winner.overlap.getArea() < dd.overlap.getArea()) {
2015                             winner = dd;
2016                         }
2017                     }
2018                 }
2019             }
2020
2021             return winner;
2022         },
2023
2024         /**
2025          * Refreshes the cache of the top-left and bottom-right points of the
2026          * drag and drop objects in the specified group(s).  This is in the
2027          * format that is stored in the drag and drop instance, so typical
2028          * usage is:
2029          * <code>
2030          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2031          * </code>
2032          * Alternatively:
2033          * <code>
2034          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2035          * </code>
2036          * @TODO this really should be an indexed array.  Alternatively this
2037          * method could accept both.
2038          * @method refreshCache
2039          * @param {Object} groups an associative array of groups to refresh
2040          * @static
2041          */
2042         refreshCache: function(groups) {
2043             for (var sGroup in groups) {
2044                 if ("string" != typeof sGroup) {
2045                     continue;
2046                 }
2047                 for (var i in this.ids[sGroup]) {
2048                     var oDD = this.ids[sGroup][i];
2049
2050                     if (this.isTypeOfDD(oDD)) {
2051                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2052                         var loc = this.getLocation(oDD);
2053                         if (loc) {
2054                             this.locationCache[oDD.id] = loc;
2055                         } else {
2056                             delete this.locationCache[oDD.id];
2057                             // this will unregister the drag and drop object if
2058                             // the element is not in a usable state
2059                             // oDD.unreg();
2060                         }
2061                     }
2062                 }
2063             }
2064         },
2065
2066         /**
2067          * This checks to make sure an element exists and is in the DOM.  The
2068          * main purpose is to handle cases where innerHTML is used to remove
2069          * drag and drop objects from the DOM.  IE provides an 'unspecified
2070          * error' when trying to access the offsetParent of such an element
2071          * @method verifyEl
2072          * @param {HTMLElement} el the element to check
2073          * @return {boolean} true if the element looks usable
2074          * @static
2075          */
2076         verifyEl: function(el) {
2077             if (el) {
2078                 var parent;
2079                 if(Roo.isIE){
2080                     try{
2081                         parent = el.offsetParent;
2082                     }catch(e){}
2083                 }else{
2084                     parent = el.offsetParent;
2085                 }
2086                 if (parent) {
2087                     return true;
2088                 }
2089             }
2090
2091             return false;
2092         },
2093
2094         /**
2095          * Returns a Region object containing the drag and drop element's position
2096          * and size, including the padding configured for it
2097          * @method getLocation
2098          * @param {DragDrop} oDD the drag and drop object to get the
2099          *                       location for
2100          * @return {Roo.lib.Region} a Region object representing the total area
2101          *                             the element occupies, including any padding
2102          *                             the instance is configured for.
2103          * @static
2104          */
2105         getLocation: function(oDD) {
2106             if (! this.isTypeOfDD(oDD)) {
2107                 return null;
2108             }
2109
2110             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2111
2112             try {
2113                 pos= Roo.lib.Dom.getXY(el);
2114             } catch (e) { }
2115
2116             if (!pos) {
2117                 return null;
2118             }
2119
2120             x1 = pos[0];
2121             x2 = x1 + el.offsetWidth;
2122             y1 = pos[1];
2123             y2 = y1 + el.offsetHeight;
2124
2125             t = y1 - oDD.padding[0];
2126             r = x2 + oDD.padding[1];
2127             b = y2 + oDD.padding[2];
2128             l = x1 - oDD.padding[3];
2129
2130             return new Roo.lib.Region( t, r, b, l );
2131         },
2132
2133         /**
2134          * Checks the cursor location to see if it over the target
2135          * @method isOverTarget
2136          * @param {Roo.lib.Point} pt The point to evaluate
2137          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2138          * @return {boolean} true if the mouse is over the target
2139          * @private
2140          * @static
2141          */
2142         isOverTarget: function(pt, oTarget, intersect) {
2143             // use cache if available
2144             var loc = this.locationCache[oTarget.id];
2145             if (!loc || !this.useCache) {
2146                 loc = this.getLocation(oTarget);
2147                 this.locationCache[oTarget.id] = loc;
2148
2149             }
2150
2151             if (!loc) {
2152                 return false;
2153             }
2154
2155             oTarget.cursorIsOver = loc.contains( pt );
2156
2157             // DragDrop is using this as a sanity check for the initial mousedown
2158             // in this case we are done.  In POINT mode, if the drag obj has no
2159             // contraints, we are also done. Otherwise we need to evaluate the
2160             // location of the target as related to the actual location of the
2161             // dragged element.
2162             var dc = this.dragCurrent;
2163             if (!dc || !dc.getTargetCoord ||
2164                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2165                 return oTarget.cursorIsOver;
2166             }
2167
2168             oTarget.overlap = null;
2169
2170             // Get the current location of the drag element, this is the
2171             // location of the mouse event less the delta that represents
2172             // where the original mousedown happened on the element.  We
2173             // need to consider constraints and ticks as well.
2174             var pos = dc.getTargetCoord(pt.x, pt.y);
2175
2176             var el = dc.getDragEl();
2177             var curRegion = new Roo.lib.Region( pos.y,
2178                                                    pos.x + el.offsetWidth,
2179                                                    pos.y + el.offsetHeight,
2180                                                    pos.x );
2181
2182             var overlap = curRegion.intersect(loc);
2183
2184             if (overlap) {
2185                 oTarget.overlap = overlap;
2186                 return (intersect) ? true : oTarget.cursorIsOver;
2187             } else {
2188                 return false;
2189             }
2190         },
2191
2192         /**
2193          * unload event handler
2194          * @method _onUnload
2195          * @private
2196          * @static
2197          */
2198         _onUnload: function(e, me) {
2199             Roo.dd.DragDropMgr.unregAll();
2200         },
2201
2202         /**
2203          * Cleans up the drag and drop events and objects.
2204          * @method unregAll
2205          * @private
2206          * @static
2207          */
2208         unregAll: function() {
2209
2210             if (this.dragCurrent) {
2211                 this.stopDrag();
2212                 this.dragCurrent = null;
2213             }
2214
2215             this._execOnAll("unreg", []);
2216
2217             for (i in this.elementCache) {
2218                 delete this.elementCache[i];
2219             }
2220
2221             this.elementCache = {};
2222             this.ids = {};
2223         },
2224
2225         /**
2226          * A cache of DOM elements
2227          * @property elementCache
2228          * @private
2229          * @static
2230          */
2231         elementCache: {},
2232
2233         /**
2234          * Get the wrapper for the DOM element specified
2235          * @method getElWrapper
2236          * @param {String} id the id of the element to get
2237          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2238          * @private
2239          * @deprecated This wrapper isn't that useful
2240          * @static
2241          */
2242         getElWrapper: function(id) {
2243             var oWrapper = this.elementCache[id];
2244             if (!oWrapper || !oWrapper.el) {
2245                 oWrapper = this.elementCache[id] =
2246                     new this.ElementWrapper(Roo.getDom(id));
2247             }
2248             return oWrapper;
2249         },
2250
2251         /**
2252          * Returns the actual DOM element
2253          * @method getElement
2254          * @param {String} id the id of the elment to get
2255          * @return {Object} The element
2256          * @deprecated use Roo.getDom instead
2257          * @static
2258          */
2259         getElement: function(id) {
2260             return Roo.getDom(id);
2261         },
2262
2263         /**
2264          * Returns the style property for the DOM element (i.e.,
2265          * document.getElById(id).style)
2266          * @method getCss
2267          * @param {String} id the id of the elment to get
2268          * @return {Object} The style property of the element
2269          * @deprecated use Roo.getDom instead
2270          * @static
2271          */
2272         getCss: function(id) {
2273             var el = Roo.getDom(id);
2274             return (el) ? el.style : null;
2275         },
2276
2277         /**
2278          * Inner class for cached elements
2279          * @class DragDropMgr.ElementWrapper
2280          * @for DragDropMgr
2281          * @private
2282          * @deprecated
2283          */
2284         ElementWrapper: function(el) {
2285                 /**
2286                  * The element
2287                  * @property el
2288                  */
2289                 this.el = el || null;
2290                 /**
2291                  * The element id
2292                  * @property id
2293                  */
2294                 this.id = this.el && el.id;
2295                 /**
2296                  * A reference to the style property
2297                  * @property css
2298                  */
2299                 this.css = this.el && el.style;
2300             },
2301
2302         /**
2303          * Returns the X position of an html element
2304          * @method getPosX
2305          * @param el the element for which to get the position
2306          * @return {int} the X coordinate
2307          * @for DragDropMgr
2308          * @deprecated use Roo.lib.Dom.getX instead
2309          * @static
2310          */
2311         getPosX: function(el) {
2312             return Roo.lib.Dom.getX(el);
2313         },
2314
2315         /**
2316          * Returns the Y position of an html element
2317          * @method getPosY
2318          * @param el the element for which to get the position
2319          * @return {int} the Y coordinate
2320          * @deprecated use Roo.lib.Dom.getY instead
2321          * @static
2322          */
2323         getPosY: function(el) {
2324             return Roo.lib.Dom.getY(el);
2325         },
2326
2327         /**
2328          * Swap two nodes.  In IE, we use the native method, for others we
2329          * emulate the IE behavior
2330          * @method swapNode
2331          * @param n1 the first node to swap
2332          * @param n2 the other node to swap
2333          * @static
2334          */
2335         swapNode: function(n1, n2) {
2336             if (n1.swapNode) {
2337                 n1.swapNode(n2);
2338             } else {
2339                 var p = n2.parentNode;
2340                 var s = n2.nextSibling;
2341
2342                 if (s == n1) {
2343                     p.insertBefore(n1, n2);
2344                 } else if (n2 == n1.nextSibling) {
2345                     p.insertBefore(n2, n1);
2346                 } else {
2347                     n1.parentNode.replaceChild(n2, n1);
2348                     p.insertBefore(n1, s);
2349                 }
2350             }
2351         },
2352
2353         /**
2354          * Returns the current scroll position
2355          * @method getScroll
2356          * @private
2357          * @static
2358          */
2359         getScroll: function () {
2360             var t, l, dde=document.documentElement, db=document.body;
2361             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2362                 t = dde.scrollTop;
2363                 l = dde.scrollLeft;
2364             } else if (db) {
2365                 t = db.scrollTop;
2366                 l = db.scrollLeft;
2367             } else {
2368
2369             }
2370             return { top: t, left: l };
2371         },
2372
2373         /**
2374          * Returns the specified element style property
2375          * @method getStyle
2376          * @param {HTMLElement} el          the element
2377          * @param {string}      styleProp   the style property
2378          * @return {string} The value of the style property
2379          * @deprecated use Roo.lib.Dom.getStyle
2380          * @static
2381          */
2382         getStyle: function(el, styleProp) {
2383             return Roo.fly(el).getStyle(styleProp);
2384         },
2385
2386         /**
2387          * Gets the scrollTop
2388          * @method getScrollTop
2389          * @return {int} the document's scrollTop
2390          * @static
2391          */
2392         getScrollTop: function () { return this.getScroll().top; },
2393
2394         /**
2395          * Gets the scrollLeft
2396          * @method getScrollLeft
2397          * @return {int} the document's scrollTop
2398          * @static
2399          */
2400         getScrollLeft: function () { return this.getScroll().left; },
2401
2402         /**
2403          * Sets the x/y position of an element to the location of the
2404          * target element.
2405          * @method moveToEl
2406          * @param {HTMLElement} moveEl      The element to move
2407          * @param {HTMLElement} targetEl    The position reference element
2408          * @static
2409          */
2410         moveToEl: function (moveEl, targetEl) {
2411             var aCoord = Roo.lib.Dom.getXY(targetEl);
2412             Roo.lib.Dom.setXY(moveEl, aCoord);
2413         },
2414
2415         /**
2416          * Numeric array sort function
2417          * @method numericSort
2418          * @static
2419          */
2420         numericSort: function(a, b) { return (a - b); },
2421
2422         /**
2423          * Internal counter
2424          * @property _timeoutCount
2425          * @private
2426          * @static
2427          */
2428         _timeoutCount: 0,
2429
2430         /**
2431          * Trying to make the load order less important.  Without this we get
2432          * an error if this file is loaded before the Event Utility.
2433          * @method _addListeners
2434          * @private
2435          * @static
2436          */
2437         _addListeners: function() {
2438             var DDM = Roo.dd.DDM;
2439             if ( Roo.lib.Event && document ) {
2440                 DDM._onLoad();
2441             } else {
2442                 if (DDM._timeoutCount > 2000) {
2443                 } else {
2444                     setTimeout(DDM._addListeners, 10);
2445                     if (document && document.body) {
2446                         DDM._timeoutCount += 1;
2447                     }
2448                 }
2449             }
2450         },
2451
2452         /**
2453          * Recursively searches the immediate parent and all child nodes for
2454          * the handle element in order to determine wheter or not it was
2455          * clicked.
2456          * @method handleWasClicked
2457          * @param node the html element to inspect
2458          * @static
2459          */
2460         handleWasClicked: function(node, id) {
2461             if (this.isHandle(id, node.id)) {
2462                 return true;
2463             } else {
2464                 // check to see if this is a text node child of the one we want
2465                 var p = node.parentNode;
2466
2467                 while (p) {
2468                     if (this.isHandle(id, p.id)) {
2469                         return true;
2470                     } else {
2471                         p = p.parentNode;
2472                     }
2473                 }
2474             }
2475
2476             return false;
2477         }
2478
2479     };
2480
2481 }();
2482
2483 // shorter alias, save a few bytes
2484 Roo.dd.DDM = Roo.dd.DragDropMgr;
2485 Roo.dd.DDM._addListeners();
2486
2487 }/*
2488  * Based on:
2489  * Ext JS Library 1.1.1
2490  * Copyright(c) 2006-2007, Ext JS, LLC.
2491  *
2492  * Originally Released Under LGPL - original licence link has changed is not relivant.
2493  *
2494  * Fork - LGPL
2495  * <script type="text/javascript">
2496  */
2497
2498 /**
2499  * @class Roo.dd.DD
2500  * A DragDrop implementation where the linked element follows the
2501  * mouse cursor during a drag.
2502  * @extends Roo.dd.DragDrop
2503  * @constructor
2504  * @param {String} id the id of the linked element
2505  * @param {String} sGroup the group of related DragDrop items
2506  * @param {object} config an object containing configurable attributes
2507  *                Valid properties for DD:
2508  *                    scroll
2509  */
2510 Roo.dd.DD = function(id, sGroup, config) {
2511     if (id) {
2512         this.init(id, sGroup, config);
2513     }
2514 };
2515
2516 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2517
2518     /**
2519      * When set to true, the utility automatically tries to scroll the browser
2520      * window wehn a drag and drop element is dragged near the viewport boundary.
2521      * Defaults to true.
2522      * @property scroll
2523      * @type boolean
2524      */
2525     scroll: true,
2526
2527     /**
2528      * Sets the pointer offset to the distance between the linked element's top
2529      * left corner and the location the element was clicked
2530      * @method autoOffset
2531      * @param {int} iPageX the X coordinate of the click
2532      * @param {int} iPageY the Y coordinate of the click
2533      */
2534     autoOffset: function(iPageX, iPageY) {
2535         var x = iPageX - this.startPageX;
2536         var y = iPageY - this.startPageY;
2537         this.setDelta(x, y);
2538     },
2539
2540     /**
2541      * Sets the pointer offset.  You can call this directly to force the
2542      * offset to be in a particular location (e.g., pass in 0,0 to set it
2543      * to the center of the object)
2544      * @method setDelta
2545      * @param {int} iDeltaX the distance from the left
2546      * @param {int} iDeltaY the distance from the top
2547      */
2548     setDelta: function(iDeltaX, iDeltaY) {
2549         this.deltaX = iDeltaX;
2550         this.deltaY = iDeltaY;
2551     },
2552
2553     /**
2554      * Sets the drag element to the location of the mousedown or click event,
2555      * maintaining the cursor location relative to the location on the element
2556      * that was clicked.  Override this if you want to place the element in a
2557      * location other than where the cursor is.
2558      * @method setDragElPos
2559      * @param {int} iPageX the X coordinate of the mousedown or drag event
2560      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2561      */
2562     setDragElPos: function(iPageX, iPageY) {
2563         // the first time we do this, we are going to check to make sure
2564         // the element has css positioning
2565
2566         var el = this.getDragEl();
2567         this.alignElWithMouse(el, iPageX, iPageY);
2568     },
2569
2570     /**
2571      * Sets the element to the location of the mousedown or click event,
2572      * maintaining the cursor location relative to the location on the element
2573      * that was clicked.  Override this if you want to place the element in a
2574      * location other than where the cursor is.
2575      * @method alignElWithMouse
2576      * @param {HTMLElement} el the element to move
2577      * @param {int} iPageX the X coordinate of the mousedown or drag event
2578      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2579      */
2580     alignElWithMouse: function(el, iPageX, iPageY) {
2581         var oCoord = this.getTargetCoord(iPageX, iPageY);
2582         var fly = el.dom ? el : Roo.fly(el);
2583         if (!this.deltaSetXY) {
2584             var aCoord = [oCoord.x, oCoord.y];
2585             fly.setXY(aCoord);
2586             var newLeft = fly.getLeft(true);
2587             var newTop  = fly.getTop(true);
2588             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2589         } else {
2590             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2591         }
2592
2593         this.cachePosition(oCoord.x, oCoord.y);
2594         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2595         return oCoord;
2596     },
2597
2598     /**
2599      * Saves the most recent position so that we can reset the constraints and
2600      * tick marks on-demand.  We need to know this so that we can calculate the
2601      * number of pixels the element is offset from its original position.
2602      * @method cachePosition
2603      * @param iPageX the current x position (optional, this just makes it so we
2604      * don't have to look it up again)
2605      * @param iPageY the current y position (optional, this just makes it so we
2606      * don't have to look it up again)
2607      */
2608     cachePosition: function(iPageX, iPageY) {
2609         if (iPageX) {
2610             this.lastPageX = iPageX;
2611             this.lastPageY = iPageY;
2612         } else {
2613             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2614             this.lastPageX = aCoord[0];
2615             this.lastPageY = aCoord[1];
2616         }
2617     },
2618
2619     /**
2620      * Auto-scroll the window if the dragged object has been moved beyond the
2621      * visible window boundary.
2622      * @method autoScroll
2623      * @param {int} x the drag element's x position
2624      * @param {int} y the drag element's y position
2625      * @param {int} h the height of the drag element
2626      * @param {int} w the width of the drag element
2627      * @private
2628      */
2629     autoScroll: function(x, y, h, w) {
2630
2631         if (this.scroll) {
2632             // The client height
2633             var clientH = Roo.lib.Dom.getViewWidth();
2634
2635             // The client width
2636             var clientW = Roo.lib.Dom.getViewHeight();
2637
2638             // The amt scrolled down
2639             var st = this.DDM.getScrollTop();
2640
2641             // The amt scrolled right
2642             var sl = this.DDM.getScrollLeft();
2643
2644             // Location of the bottom of the element
2645             var bot = h + y;
2646
2647             // Location of the right of the element
2648             var right = w + x;
2649
2650             // The distance from the cursor to the bottom of the visible area,
2651             // adjusted so that we don't scroll if the cursor is beyond the
2652             // element drag constraints
2653             var toBot = (clientH + st - y - this.deltaY);
2654
2655             // The distance from the cursor to the right of the visible area
2656             var toRight = (clientW + sl - x - this.deltaX);
2657
2658
2659             // How close to the edge the cursor must be before we scroll
2660             // var thresh = (document.all) ? 100 : 40;
2661             var thresh = 40;
2662
2663             // How many pixels to scroll per autoscroll op.  This helps to reduce
2664             // clunky scrolling. IE is more sensitive about this ... it needs this
2665             // value to be higher.
2666             var scrAmt = (document.all) ? 80 : 30;
2667
2668             // Scroll down if we are near the bottom of the visible page and the
2669             // obj extends below the crease
2670             if ( bot > clientH && toBot < thresh ) {
2671                 window.scrollTo(sl, st + scrAmt);
2672             }
2673
2674             // Scroll up if the window is scrolled down and the top of the object
2675             // goes above the top border
2676             if ( y < st && st > 0 && y - st < thresh ) {
2677                 window.scrollTo(sl, st - scrAmt);
2678             }
2679
2680             // Scroll right if the obj is beyond the right border and the cursor is
2681             // near the border.
2682             if ( right > clientW && toRight < thresh ) {
2683                 window.scrollTo(sl + scrAmt, st);
2684             }
2685
2686             // Scroll left if the window has been scrolled to the right and the obj
2687             // extends past the left border
2688             if ( x < sl && sl > 0 && x - sl < thresh ) {
2689                 window.scrollTo(sl - scrAmt, st);
2690             }
2691         }
2692     },
2693
2694     /**
2695      * Finds the location the element should be placed if we want to move
2696      * it to where the mouse location less the click offset would place us.
2697      * @method getTargetCoord
2698      * @param {int} iPageX the X coordinate of the click
2699      * @param {int} iPageY the Y coordinate of the click
2700      * @return an object that contains the coordinates (Object.x and Object.y)
2701      * @private
2702      */
2703     getTargetCoord: function(iPageX, iPageY) {
2704
2705
2706         var x = iPageX - this.deltaX;
2707         var y = iPageY - this.deltaY;
2708
2709         if (this.constrainX) {
2710             if (x < this.minX) { x = this.minX; }
2711             if (x > this.maxX) { x = this.maxX; }
2712         }
2713
2714         if (this.constrainY) {
2715             if (y < this.minY) { y = this.minY; }
2716             if (y > this.maxY) { y = this.maxY; }
2717         }
2718
2719         x = this.getTick(x, this.xTicks);
2720         y = this.getTick(y, this.yTicks);
2721
2722
2723         return {x:x, y:y};
2724     },
2725
2726     /*
2727      * Sets up config options specific to this class. Overrides
2728      * Roo.dd.DragDrop, but all versions of this method through the
2729      * inheritance chain are called
2730      */
2731     applyConfig: function() {
2732         Roo.dd.DD.superclass.applyConfig.call(this);
2733         this.scroll = (this.config.scroll !== false);
2734     },
2735
2736     /*
2737      * Event that fires prior to the onMouseDown event.  Overrides
2738      * Roo.dd.DragDrop.
2739      */
2740     b4MouseDown: function(e) {
2741         // this.resetConstraints();
2742         this.autoOffset(e.getPageX(),
2743                             e.getPageY());
2744     },
2745
2746     /*
2747      * Event that fires prior to the onDrag event.  Overrides
2748      * Roo.dd.DragDrop.
2749      */
2750     b4Drag: function(e) {
2751         this.setDragElPos(e.getPageX(),
2752                             e.getPageY());
2753     },
2754
2755     toString: function() {
2756         return ("DD " + this.id);
2757     }
2758
2759     //////////////////////////////////////////////////////////////////////////
2760     // Debugging ygDragDrop events that can be overridden
2761     //////////////////////////////////////////////////////////////////////////
2762     /*
2763     startDrag: function(x, y) {
2764     },
2765
2766     onDrag: function(e) {
2767     },
2768
2769     onDragEnter: function(e, id) {
2770     },
2771
2772     onDragOver: function(e, id) {
2773     },
2774
2775     onDragOut: function(e, id) {
2776     },
2777
2778     onDragDrop: function(e, id) {
2779     },
2780
2781     endDrag: function(e) {
2782     }
2783
2784     */
2785
2786 });/*
2787  * Based on:
2788  * Ext JS Library 1.1.1
2789  * Copyright(c) 2006-2007, Ext JS, LLC.
2790  *
2791  * Originally Released Under LGPL - original licence link has changed is not relivant.
2792  *
2793  * Fork - LGPL
2794  * <script type="text/javascript">
2795  */
2796
2797 /**
2798  * @class Roo.dd.DDProxy
2799  * A DragDrop implementation that inserts an empty, bordered div into
2800  * the document that follows the cursor during drag operations.  At the time of
2801  * the click, the frame div is resized to the dimensions of the linked html
2802  * element, and moved to the exact location of the linked element.
2803  *
2804  * References to the "frame" element refer to the single proxy element that
2805  * was created to be dragged in place of all DDProxy elements on the
2806  * page.
2807  *
2808  * @extends Roo.dd.DD
2809  * @constructor
2810  * @param {String} id the id of the linked html element
2811  * @param {String} sGroup the group of related DragDrop objects
2812  * @param {object} config an object containing configurable attributes
2813  *                Valid properties for DDProxy in addition to those in DragDrop:
2814  *                   resizeFrame, centerFrame, dragElId
2815  */
2816 Roo.dd.DDProxy = function(id, sGroup, config) {
2817     if (id) {
2818         this.init(id, sGroup, config);
2819         this.initFrame();
2820     }
2821 };
2822
2823 /**
2824  * The default drag frame div id
2825  * @property Roo.dd.DDProxy.dragElId
2826  * @type String
2827  * @static
2828  */
2829 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2830
2831 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2832
2833     /**
2834      * By default we resize the drag frame to be the same size as the element
2835      * we want to drag (this is to get the frame effect).  We can turn it off
2836      * if we want a different behavior.
2837      * @property resizeFrame
2838      * @type boolean
2839      */
2840     resizeFrame: true,
2841
2842     /**
2843      * By default the frame is positioned exactly where the drag element is, so
2844      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2845      * you do not have constraints on the obj is to have the drag frame centered
2846      * around the cursor.  Set centerFrame to true for this effect.
2847      * @property centerFrame
2848      * @type boolean
2849      */
2850     centerFrame: false,
2851
2852     /**
2853      * Creates the proxy element if it does not yet exist
2854      * @method createFrame
2855      */
2856     createFrame: function() {
2857         var self = this;
2858         var body = document.body;
2859
2860         if (!body || !body.firstChild) {
2861             setTimeout( function() { self.createFrame(); }, 50 );
2862             return;
2863         }
2864
2865         var div = this.getDragEl();
2866
2867         if (!div) {
2868             div    = document.createElement("div");
2869             div.id = this.dragElId;
2870             var s  = div.style;
2871
2872             s.position   = "absolute";
2873             s.visibility = "hidden";
2874             s.cursor     = "move";
2875             s.border     = "2px solid #aaa";
2876             s.zIndex     = 999;
2877
2878             // appendChild can blow up IE if invoked prior to the window load event
2879             // while rendering a table.  It is possible there are other scenarios
2880             // that would cause this to happen as well.
2881             body.insertBefore(div, body.firstChild);
2882         }
2883     },
2884
2885     /**
2886      * Initialization for the drag frame element.  Must be called in the
2887      * constructor of all subclasses
2888      * @method initFrame
2889      */
2890     initFrame: function() {
2891         this.createFrame();
2892     },
2893
2894     applyConfig: function() {
2895         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2896
2897         this.resizeFrame = (this.config.resizeFrame !== false);
2898         this.centerFrame = (this.config.centerFrame);
2899         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2900     },
2901
2902     /**
2903      * Resizes the drag frame to the dimensions of the clicked object, positions
2904      * it over the object, and finally displays it
2905      * @method showFrame
2906      * @param {int} iPageX X click position
2907      * @param {int} iPageY Y click position
2908      * @private
2909      */
2910     showFrame: function(iPageX, iPageY) {
2911         var el = this.getEl();
2912         var dragEl = this.getDragEl();
2913         var s = dragEl.style;
2914
2915         this._resizeProxy();
2916
2917         if (this.centerFrame) {
2918             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2919                            Math.round(parseInt(s.height, 10)/2) );
2920         }
2921
2922         this.setDragElPos(iPageX, iPageY);
2923
2924         Roo.fly(dragEl).show();
2925     },
2926
2927     /**
2928      * The proxy is automatically resized to the dimensions of the linked
2929      * element when a drag is initiated, unless resizeFrame is set to false
2930      * @method _resizeProxy
2931      * @private
2932      */
2933     _resizeProxy: function() {
2934         if (this.resizeFrame) {
2935             var el = this.getEl();
2936             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2937         }
2938     },
2939
2940     // overrides Roo.dd.DragDrop
2941     b4MouseDown: function(e) {
2942         var x = e.getPageX();
2943         var y = e.getPageY();
2944         this.autoOffset(x, y);
2945         this.setDragElPos(x, y);
2946     },
2947
2948     // overrides Roo.dd.DragDrop
2949     b4StartDrag: function(x, y) {
2950         // show the drag frame
2951         this.showFrame(x, y);
2952     },
2953
2954     // overrides Roo.dd.DragDrop
2955     b4EndDrag: function(e) {
2956         Roo.fly(this.getDragEl()).hide();
2957     },
2958
2959     // overrides Roo.dd.DragDrop
2960     // By default we try to move the element to the last location of the frame.
2961     // This is so that the default behavior mirrors that of Roo.dd.DD.
2962     endDrag: function(e) {
2963
2964         var lel = this.getEl();
2965         var del = this.getDragEl();
2966
2967         // Show the drag frame briefly so we can get its position
2968         del.style.visibility = "";
2969
2970         this.beforeMove();
2971         // Hide the linked element before the move to get around a Safari
2972         // rendering bug.
2973         lel.style.visibility = "hidden";
2974         Roo.dd.DDM.moveToEl(lel, del);
2975         del.style.visibility = "hidden";
2976         lel.style.visibility = "";
2977
2978         this.afterDrag();
2979     },
2980
2981     beforeMove : function(){
2982
2983     },
2984
2985     afterDrag : function(){
2986
2987     },
2988
2989     toString: function() {
2990         return ("DDProxy " + this.id);
2991     }
2992
2993 });
2994 /*
2995  * Based on:
2996  * Ext JS Library 1.1.1
2997  * Copyright(c) 2006-2007, Ext JS, LLC.
2998  *
2999  * Originally Released Under LGPL - original licence link has changed is not relivant.
3000  *
3001  * Fork - LGPL
3002  * <script type="text/javascript">
3003  */
3004
3005  /**
3006  * @class Roo.dd.DDTarget
3007  * A DragDrop implementation that does not move, but can be a drop
3008  * target.  You would get the same result by simply omitting implementation
3009  * for the event callbacks, but this way we reduce the processing cost of the
3010  * event listener and the callbacks.
3011  * @extends Roo.dd.DragDrop
3012  * @constructor
3013  * @param {String} id the id of the element that is a drop target
3014  * @param {String} sGroup the group of related DragDrop objects
3015  * @param {object} config an object containing configurable attributes
3016  *                 Valid properties for DDTarget in addition to those in
3017  *                 DragDrop:
3018  *                    none
3019  */
3020 Roo.dd.DDTarget = function(id, sGroup, config) {
3021     if (id) {
3022         this.initTarget(id, sGroup, config);
3023     }
3024     if (config.listeners || config.events) { 
3025        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
3026             listeners : config.listeners || {}, 
3027             events : config.events || {} 
3028         });    
3029     }
3030 };
3031
3032 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3033 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3034     toString: function() {
3035         return ("DDTarget " + this.id);
3036     }
3037 });
3038 /*
3039  * Based on:
3040  * Ext JS Library 1.1.1
3041  * Copyright(c) 2006-2007, Ext JS, LLC.
3042  *
3043  * Originally Released Under LGPL - original licence link has changed is not relivant.
3044  *
3045  * Fork - LGPL
3046  * <script type="text/javascript">
3047  */
3048  
3049
3050 /**
3051  * @class Roo.dd.ScrollManager
3052  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3053  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3054  * @singleton
3055  */
3056 Roo.dd.ScrollManager = function(){
3057     var ddm = Roo.dd.DragDropMgr;
3058     var els = {};
3059     var dragEl = null;
3060     var proc = {};
3061     
3062     
3063     
3064     var onStop = function(e){
3065         dragEl = null;
3066         clearProc();
3067     };
3068     
3069     var triggerRefresh = function(){
3070         if(ddm.dragCurrent){
3071              ddm.refreshCache(ddm.dragCurrent.groups);
3072         }
3073     };
3074     
3075     var doScroll = function(){
3076         if(ddm.dragCurrent){
3077             var dds = Roo.dd.ScrollManager;
3078             if(!dds.animate){
3079                 if(proc.el.scroll(proc.dir, dds.increment)){
3080                     triggerRefresh();
3081                 }
3082             }else{
3083                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3084             }
3085         }
3086     };
3087     
3088     var clearProc = function(){
3089         if(proc.id){
3090             clearInterval(proc.id);
3091         }
3092         proc.id = 0;
3093         proc.el = null;
3094         proc.dir = "";
3095     };
3096     
3097     var startProc = function(el, dir){
3098          Roo.log('scroll startproc');
3099         clearProc();
3100         proc.el = el;
3101         proc.dir = dir;
3102         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3103     };
3104     
3105     var onFire = function(e, isDrop){
3106        
3107         if(isDrop || !ddm.dragCurrent){ return; }
3108         var dds = Roo.dd.ScrollManager;
3109         if(!dragEl || dragEl != ddm.dragCurrent){
3110             dragEl = ddm.dragCurrent;
3111             // refresh regions on drag start
3112             dds.refreshCache();
3113         }
3114         
3115         var xy = Roo.lib.Event.getXY(e);
3116         var pt = new Roo.lib.Point(xy[0], xy[1]);
3117         for(var id in els){
3118             var el = els[id], r = el._region;
3119             if(r && r.contains(pt) && el.isScrollable()){
3120                 if(r.bottom - pt.y <= dds.thresh){
3121                     if(proc.el != el){
3122                         startProc(el, "down");
3123                     }
3124                     return;
3125                 }else if(r.right - pt.x <= dds.thresh){
3126                     if(proc.el != el){
3127                         startProc(el, "left");
3128                     }
3129                     return;
3130                 }else if(pt.y - r.top <= dds.thresh){
3131                     if(proc.el != el){
3132                         startProc(el, "up");
3133                     }
3134                     return;
3135                 }else if(pt.x - r.left <= dds.thresh){
3136                     if(proc.el != el){
3137                         startProc(el, "right");
3138                     }
3139                     return;
3140                 }
3141             }
3142         }
3143         clearProc();
3144     };
3145     
3146     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3147     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3148     
3149     return {
3150         /**
3151          * Registers new overflow element(s) to auto scroll
3152          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3153          */
3154         register : function(el){
3155             if(el instanceof Array){
3156                 for(var i = 0, len = el.length; i < len; i++) {
3157                         this.register(el[i]);
3158                 }
3159             }else{
3160                 el = Roo.get(el);
3161                 els[el.id] = el;
3162             }
3163             Roo.dd.ScrollManager.els = els;
3164         },
3165         
3166         /**
3167          * Unregisters overflow element(s) so they are no longer scrolled
3168          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3169          */
3170         unregister : function(el){
3171             if(el instanceof Array){
3172                 for(var i = 0, len = el.length; i < len; i++) {
3173                         this.unregister(el[i]);
3174                 }
3175             }else{
3176                 el = Roo.get(el);
3177                 delete els[el.id];
3178             }
3179         },
3180         
3181         /**
3182          * The number of pixels from the edge of a container the pointer needs to be to 
3183          * trigger scrolling (defaults to 25)
3184          * @type Number
3185          */
3186         thresh : 25,
3187         
3188         /**
3189          * The number of pixels to scroll in each scroll increment (defaults to 50)
3190          * @type Number
3191          */
3192         increment : 100,
3193         
3194         /**
3195          * The frequency of scrolls in milliseconds (defaults to 500)
3196          * @type Number
3197          */
3198         frequency : 500,
3199         
3200         /**
3201          * True to animate the scroll (defaults to true)
3202          * @type Boolean
3203          */
3204         animate: true,
3205         
3206         /**
3207          * The animation duration in seconds - 
3208          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3209          * @type Number
3210          */
3211         animDuration: .4,
3212         
3213         /**
3214          * Manually trigger a cache refresh.
3215          */
3216         refreshCache : function(){
3217             for(var id in els){
3218                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3219                     els[id]._region = els[id].getRegion();
3220                 }
3221             }
3222         }
3223     };
3224 }();/*
3225  * Based on:
3226  * Ext JS Library 1.1.1
3227  * Copyright(c) 2006-2007, Ext JS, LLC.
3228  *
3229  * Originally Released Under LGPL - original licence link has changed is not relivant.
3230  *
3231  * Fork - LGPL
3232  * <script type="text/javascript">
3233  */
3234  
3235
3236 /**
3237  * @class Roo.dd.Registry
3238  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3239  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3240  * @singleton
3241  */
3242 Roo.dd.Registry = function(){
3243     var elements = {}; 
3244     var handles = {}; 
3245     var autoIdSeed = 0;
3246
3247     var getId = function(el, autogen){
3248         if(typeof el == "string"){
3249             return el;
3250         }
3251         var id = el.id;
3252         if(!id && autogen !== false){
3253             id = "roodd-" + (++autoIdSeed);
3254             el.id = id;
3255         }
3256         return id;
3257     };
3258     
3259     return {
3260     /**
3261      * Register a drag drop element
3262      * @param {String|HTMLElement} element The id or DOM node to register
3263      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3264      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3265      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3266      * populated in the data object (if applicable):
3267      * <pre>
3268 Value      Description<br />
3269 ---------  ------------------------------------------<br />
3270 handles    Array of DOM nodes that trigger dragging<br />
3271            for the element being registered<br />
3272 isHandle   True if the element passed in triggers<br />
3273            dragging itself, else false
3274 </pre>
3275      */
3276         register : function(el, data){
3277             data = data || {};
3278             if(typeof el == "string"){
3279                 el = document.getElementById(el);
3280             }
3281             data.ddel = el;
3282             elements[getId(el)] = data;
3283             if(data.isHandle !== false){
3284                 handles[data.ddel.id] = data;
3285             }
3286             if(data.handles){
3287                 var hs = data.handles;
3288                 for(var i = 0, len = hs.length; i < len; i++){
3289                         handles[getId(hs[i])] = data;
3290                 }
3291             }
3292         },
3293
3294     /**
3295      * Unregister a drag drop element
3296      * @param {String|HTMLElement}  element The id or DOM node to unregister
3297      */
3298         unregister : function(el){
3299             var id = getId(el, false);
3300             var data = elements[id];
3301             if(data){
3302                 delete elements[id];
3303                 if(data.handles){
3304                     var hs = data.handles;
3305                     for(var i = 0, len = hs.length; i < len; i++){
3306                         delete handles[getId(hs[i], false)];
3307                     }
3308                 }
3309             }
3310         },
3311
3312     /**
3313      * Returns the handle registered for a DOM Node by id
3314      * @param {String|HTMLElement} id The DOM node or id to look up
3315      * @return {Object} handle The custom handle data
3316      */
3317         getHandle : function(id){
3318             if(typeof id != "string"){ // must be element?
3319                 id = id.id;
3320             }
3321             return handles[id];
3322         },
3323
3324     /**
3325      * Returns the handle that is registered for the DOM node that is the target of the event
3326      * @param {Event} e The event
3327      * @return {Object} handle The custom handle data
3328      */
3329         getHandleFromEvent : function(e){
3330             var t = Roo.lib.Event.getTarget(e);
3331             return t ? handles[t.id] : null;
3332         },
3333
3334     /**
3335      * Returns a custom data object that is registered for a DOM node by id
3336      * @param {String|HTMLElement} id The DOM node or id to look up
3337      * @return {Object} data The custom data
3338      */
3339         getTarget : function(id){
3340             if(typeof id != "string"){ // must be element?
3341                 id = id.id;
3342             }
3343             return elements[id];
3344         },
3345
3346     /**
3347      * Returns a custom data object that is registered for the DOM node that is the target of the event
3348      * @param {Event} e The event
3349      * @return {Object} data The custom data
3350      */
3351         getTargetFromEvent : function(e){
3352             var t = Roo.lib.Event.getTarget(e);
3353             return t ? elements[t.id] || handles[t.id] : null;
3354         }
3355     };
3356 }();/*
3357  * Based on:
3358  * Ext JS Library 1.1.1
3359  * Copyright(c) 2006-2007, Ext JS, LLC.
3360  *
3361  * Originally Released Under LGPL - original licence link has changed is not relivant.
3362  *
3363  * Fork - LGPL
3364  * <script type="text/javascript">
3365  */
3366  
3367
3368 /**
3369  * @class Roo.dd.StatusProxy
3370  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3371  * default drag proxy used by all Roo.dd components.
3372  * @constructor
3373  * @param {Object} config
3374  */
3375 Roo.dd.StatusProxy = function(config){
3376     Roo.apply(this, config);
3377     this.id = this.id || Roo.id();
3378     this.el = new Roo.Layer({
3379         dh: {
3380             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3381                 {tag: "div", cls: "x-dd-drop-icon"},
3382                 {tag: "div", cls: "x-dd-drag-ghost"}
3383             ]
3384         }, 
3385         shadow: !config || config.shadow !== false
3386     });
3387     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3388     this.dropStatus = this.dropNotAllowed;
3389 };
3390
3391 Roo.dd.StatusProxy.prototype = {
3392     /**
3393      * @cfg {String} dropAllowed
3394      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3395      */
3396     dropAllowed : "x-dd-drop-ok",
3397     /**
3398      * @cfg {String} dropNotAllowed
3399      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3400      */
3401     dropNotAllowed : "x-dd-drop-nodrop",
3402
3403     /**
3404      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3405      * over the current target element.
3406      * @param {String} cssClass The css class for the new drop status indicator image
3407      */
3408     setStatus : function(cssClass){
3409         cssClass = cssClass || this.dropNotAllowed;
3410         if(this.dropStatus != cssClass){
3411             this.el.replaceClass(this.dropStatus, cssClass);
3412             this.dropStatus = cssClass;
3413         }
3414     },
3415
3416     /**
3417      * Resets the status indicator to the default dropNotAllowed value
3418      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3419      */
3420     reset : function(clearGhost){
3421         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3422         this.dropStatus = this.dropNotAllowed;
3423         if(clearGhost){
3424             this.ghost.update("");
3425         }
3426     },
3427
3428     /**
3429      * Updates the contents of the ghost element
3430      * @param {String} html The html that will replace the current innerHTML of the ghost element
3431      */
3432     update : function(html){
3433         if(typeof html == "string"){
3434             this.ghost.update(html);
3435         }else{
3436             this.ghost.update("");
3437             html.style.margin = "0";
3438             this.ghost.dom.appendChild(html);
3439         }
3440         // ensure float = none set?? cant remember why though.
3441         var el = this.ghost.dom.firstChild;
3442                 if(el){
3443                         Roo.fly(el).setStyle('float', 'none');
3444                 }
3445     },
3446     
3447     /**
3448      * Returns the underlying proxy {@link Roo.Layer}
3449      * @return {Roo.Layer} el
3450     */
3451     getEl : function(){
3452         return this.el;
3453     },
3454
3455     /**
3456      * Returns the ghost element
3457      * @return {Roo.Element} el
3458      */
3459     getGhost : function(){
3460         return this.ghost;
3461     },
3462
3463     /**
3464      * Hides the proxy
3465      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3466      */
3467     hide : function(clear){
3468         this.el.hide();
3469         if(clear){
3470             this.reset(true);
3471         }
3472     },
3473
3474     /**
3475      * Stops the repair animation if it's currently running
3476      */
3477     stop : function(){
3478         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3479             this.anim.stop();
3480         }
3481     },
3482
3483     /**
3484      * Displays this proxy
3485      */
3486     show : function(){
3487         this.el.show();
3488     },
3489
3490     /**
3491      * Force the Layer to sync its shadow and shim positions to the element
3492      */
3493     sync : function(){
3494         this.el.sync();
3495     },
3496
3497     /**
3498      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3499      * invalid drop operation by the item being dragged.
3500      * @param {Array} xy The XY position of the element ([x, y])
3501      * @param {Function} callback The function to call after the repair is complete
3502      * @param {Object} scope The scope in which to execute the callback
3503      */
3504     repair : function(xy, callback, scope){
3505         this.callback = callback;
3506         this.scope = scope;
3507         if(xy && this.animRepair !== false){
3508             this.el.addClass("x-dd-drag-repair");
3509             this.el.hideUnders(true);
3510             this.anim = this.el.shift({
3511                 duration: this.repairDuration || .5,
3512                 easing: 'easeOut',
3513                 xy: xy,
3514                 stopFx: true,
3515                 callback: this.afterRepair,
3516                 scope: this
3517             });
3518         }else{
3519             this.afterRepair();
3520         }
3521     },
3522
3523     // private
3524     afterRepair : function(){
3525         this.hide(true);
3526         if(typeof this.callback == "function"){
3527             this.callback.call(this.scope || this);
3528         }
3529         this.callback = null;
3530         this.scope = null;
3531     }
3532 };/*
3533  * Based on:
3534  * Ext JS Library 1.1.1
3535  * Copyright(c) 2006-2007, Ext JS, LLC.
3536  *
3537  * Originally Released Under LGPL - original licence link has changed is not relivant.
3538  *
3539  * Fork - LGPL
3540  * <script type="text/javascript">
3541  */
3542
3543 /**
3544  * @class Roo.dd.DragSource
3545  * @extends Roo.dd.DDProxy
3546  * A simple class that provides the basic implementation needed to make any element draggable.
3547  * @constructor
3548  * @param {String/HTMLElement/Element} el The container element
3549  * @param {Object} config
3550  */
3551 Roo.dd.DragSource = function(el, config){
3552     this.el = Roo.get(el);
3553     this.dragData = {};
3554     
3555     Roo.apply(this, config);
3556     
3557     if(!this.proxy){
3558         this.proxy = new Roo.dd.StatusProxy();
3559     }
3560
3561     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3562           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3563     
3564     this.dragging = false;
3565 };
3566
3567 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3568     /**
3569      * @cfg {String} dropAllowed
3570      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3571      */
3572     dropAllowed : "x-dd-drop-ok",
3573     /**
3574      * @cfg {String} dropNotAllowed
3575      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3576      */
3577     dropNotAllowed : "x-dd-drop-nodrop",
3578
3579     /**
3580      * Returns the data object associated with this drag source
3581      * @return {Object} data An object containing arbitrary data
3582      */
3583     getDragData : function(e){
3584         return this.dragData;
3585     },
3586
3587     // private
3588     onDragEnter : function(e, id){
3589         var target = Roo.dd.DragDropMgr.getDDById(id);
3590         this.cachedTarget = target;
3591         if(this.beforeDragEnter(target, e, id) !== false){
3592             if(target.isNotifyTarget){
3593                 var status = target.notifyEnter(this, e, this.dragData);
3594                 this.proxy.setStatus(status);
3595             }else{
3596                 this.proxy.setStatus(this.dropAllowed);
3597             }
3598             
3599             if(this.afterDragEnter){
3600                 /**
3601                  * An empty function by default, but provided so that you can perform a custom action
3602                  * when the dragged item enters the drop target by providing an implementation.
3603                  * @param {Roo.dd.DragDrop} target The drop target
3604                  * @param {Event} e The event object
3605                  * @param {String} id The id of the dragged element
3606                  * @method afterDragEnter
3607                  */
3608                 this.afterDragEnter(target, e, id);
3609             }
3610         }
3611     },
3612
3613     /**
3614      * An empty function by default, but provided so that you can perform a custom action
3615      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3616      * @param {Roo.dd.DragDrop} target The drop target
3617      * @param {Event} e The event object
3618      * @param {String} id The id of the dragged element
3619      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3620      */
3621     beforeDragEnter : function(target, e, id){
3622         return true;
3623     },
3624
3625     // private
3626     alignElWithMouse: function() {
3627         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3628         this.proxy.sync();
3629     },
3630
3631     // private
3632     onDragOver : function(e, id){
3633         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3634         if(this.beforeDragOver(target, e, id) !== false){
3635             if(target.isNotifyTarget){
3636                 var status = target.notifyOver(this, e, this.dragData);
3637                 this.proxy.setStatus(status);
3638             }
3639
3640             if(this.afterDragOver){
3641                 /**
3642                  * An empty function by default, but provided so that you can perform a custom action
3643                  * while the dragged item is over the drop target by providing an implementation.
3644                  * @param {Roo.dd.DragDrop} target The drop target
3645                  * @param {Event} e The event object
3646                  * @param {String} id The id of the dragged element
3647                  * @method afterDragOver
3648                  */
3649                 this.afterDragOver(target, e, id);
3650             }
3651         }
3652     },
3653
3654     /**
3655      * An empty function by default, but provided so that you can perform a custom action
3656      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3657      * @param {Roo.dd.DragDrop} target The drop target
3658      * @param {Event} e The event object
3659      * @param {String} id The id of the dragged element
3660      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3661      */
3662     beforeDragOver : function(target, e, id){
3663         return true;
3664     },
3665
3666     // private
3667     onDragOut : function(e, id){
3668         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3669         if(this.beforeDragOut(target, e, id) !== false){
3670             if(target.isNotifyTarget){
3671                 target.notifyOut(this, e, this.dragData);
3672             }
3673             this.proxy.reset();
3674             if(this.afterDragOut){
3675                 /**
3676                  * An empty function by default, but provided so that you can perform a custom action
3677                  * after the dragged item is dragged out of the target without dropping.
3678                  * @param {Roo.dd.DragDrop} target The drop target
3679                  * @param {Event} e The event object
3680                  * @param {String} id The id of the dragged element
3681                  * @method afterDragOut
3682                  */
3683                 this.afterDragOut(target, e, id);
3684             }
3685         }
3686         this.cachedTarget = null;
3687     },
3688
3689     /**
3690      * An empty function by default, but provided so that you can perform a custom action before the dragged
3691      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3692      * @param {Roo.dd.DragDrop} target The drop target
3693      * @param {Event} e The event object
3694      * @param {String} id The id of the dragged element
3695      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3696      */
3697     beforeDragOut : function(target, e, id){
3698         return true;
3699     },
3700     
3701     // private
3702     onDragDrop : function(e, id){
3703         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3704         if(this.beforeDragDrop(target, e, id) !== false){
3705             if(target.isNotifyTarget){
3706                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3707                     this.onValidDrop(target, e, id);
3708                 }else{
3709                     this.onInvalidDrop(target, e, id);
3710                 }
3711             }else{
3712                 this.onValidDrop(target, e, id);
3713             }
3714             
3715             if(this.afterDragDrop){
3716                 /**
3717                  * An empty function by default, but provided so that you can perform a custom action
3718                  * after a valid drag drop has occurred by providing an implementation.
3719                  * @param {Roo.dd.DragDrop} target The drop target
3720                  * @param {Event} e The event object
3721                  * @param {String} id The id of the dropped element
3722                  * @method afterDragDrop
3723                  */
3724                 this.afterDragDrop(target, e, id);
3725             }
3726         }
3727         delete this.cachedTarget;
3728     },
3729
3730     /**
3731      * An empty function by default, but provided so that you can perform a custom action before the dragged
3732      * item is dropped onto the target and optionally cancel the onDragDrop.
3733      * @param {Roo.dd.DragDrop} target The drop target
3734      * @param {Event} e The event object
3735      * @param {String} id The id of the dragged element
3736      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3737      */
3738     beforeDragDrop : function(target, e, id){
3739         return true;
3740     },
3741
3742     // private
3743     onValidDrop : function(target, e, id){
3744         this.hideProxy();
3745         if(this.afterValidDrop){
3746             /**
3747              * An empty function by default, but provided so that you can perform a custom action
3748              * after a valid drop has occurred by providing an implementation.
3749              * @param {Object} target The target DD 
3750              * @param {Event} e The event object
3751              * @param {String} id The id of the dropped element
3752              * @method afterInvalidDrop
3753              */
3754             this.afterValidDrop(target, e, id);
3755         }
3756     },
3757
3758     // private
3759     getRepairXY : function(e, data){
3760         return this.el.getXY();  
3761     },
3762
3763     // private
3764     onInvalidDrop : function(target, e, id){
3765         this.beforeInvalidDrop(target, e, id);
3766         if(this.cachedTarget){
3767             if(this.cachedTarget.isNotifyTarget){
3768                 this.cachedTarget.notifyOut(this, e, this.dragData);
3769             }
3770             this.cacheTarget = null;
3771         }
3772         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3773
3774         if(this.afterInvalidDrop){
3775             /**
3776              * An empty function by default, but provided so that you can perform a custom action
3777              * after an invalid drop has occurred by providing an implementation.
3778              * @param {Event} e The event object
3779              * @param {String} id The id of the dropped element
3780              * @method afterInvalidDrop
3781              */
3782             this.afterInvalidDrop(e, id);
3783         }
3784     },
3785
3786     // private
3787     afterRepair : function(){
3788         if(Roo.enableFx){
3789             this.el.highlight(this.hlColor || "c3daf9");
3790         }
3791         this.dragging = false;
3792     },
3793
3794     /**
3795      * An empty function by default, but provided so that you can perform a custom action after an invalid
3796      * drop has occurred.
3797      * @param {Roo.dd.DragDrop} target The drop target
3798      * @param {Event} e The event object
3799      * @param {String} id The id of the dragged element
3800      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3801      */
3802     beforeInvalidDrop : function(target, e, id){
3803         return true;
3804     },
3805
3806     // private
3807     handleMouseDown : function(e){
3808         if(this.dragging) {
3809             return;
3810         }
3811         var data = this.getDragData(e);
3812         if(data && this.onBeforeDrag(data, e) !== false){
3813             this.dragData = data;
3814             this.proxy.stop();
3815             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3816         } 
3817     },
3818
3819     /**
3820      * An empty function by default, but provided so that you can perform a custom action before the initial
3821      * drag event begins and optionally cancel it.
3822      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3823      * @param {Event} e The event object
3824      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3825      */
3826     onBeforeDrag : function(data, e){
3827         return true;
3828     },
3829
3830     /**
3831      * An empty function by default, but provided so that you can perform a custom action once the initial
3832      * drag event has begun.  The drag cannot be canceled from this function.
3833      * @param {Number} x The x position of the click on the dragged object
3834      * @param {Number} y The y position of the click on the dragged object
3835      */
3836     onStartDrag : Roo.emptyFn,
3837
3838     // private - YUI override
3839     startDrag : function(x, y){
3840         this.proxy.reset();
3841         this.dragging = true;
3842         this.proxy.update("");
3843         this.onInitDrag(x, y);
3844         this.proxy.show();
3845     },
3846
3847     // private
3848     onInitDrag : function(x, y){
3849         var clone = this.el.dom.cloneNode(true);
3850         clone.id = Roo.id(); // prevent duplicate ids
3851         this.proxy.update(clone);
3852         this.onStartDrag(x, y);
3853         return true;
3854     },
3855
3856     /**
3857      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3858      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3859      */
3860     getProxy : function(){
3861         return this.proxy;  
3862     },
3863
3864     /**
3865      * Hides the drag source's {@link Roo.dd.StatusProxy}
3866      */
3867     hideProxy : function(){
3868         this.proxy.hide();  
3869         this.proxy.reset(true);
3870         this.dragging = false;
3871     },
3872
3873     // private
3874     triggerCacheRefresh : function(){
3875         Roo.dd.DDM.refreshCache(this.groups);
3876     },
3877
3878     // private - override to prevent hiding
3879     b4EndDrag: function(e) {
3880     },
3881
3882     // private - override to prevent moving
3883     endDrag : function(e){
3884         this.onEndDrag(this.dragData, e);
3885     },
3886
3887     // private
3888     onEndDrag : function(data, e){
3889     },
3890     
3891     // private - pin to cursor
3892     autoOffset : function(x, y) {
3893         this.setDelta(-12, -20);
3894     }    
3895 });/*
3896  * Based on:
3897  * Ext JS Library 1.1.1
3898  * Copyright(c) 2006-2007, Ext JS, LLC.
3899  *
3900  * Originally Released Under LGPL - original licence link has changed is not relivant.
3901  *
3902  * Fork - LGPL
3903  * <script type="text/javascript">
3904  */
3905
3906
3907 /**
3908  * @class Roo.dd.DropTarget
3909  * @extends Roo.dd.DDTarget
3910  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3911  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3912  * @constructor
3913  * @param {String/HTMLElement/Element} el The container element
3914  * @param {Object} config
3915  */
3916 Roo.dd.DropTarget = function(el, config){
3917     this.el = Roo.get(el);
3918     
3919     var listeners = false; ;
3920     if (config && config.listeners) {
3921         listeners= config.listeners;
3922         delete config.listeners;
3923     }
3924     Roo.apply(this, config);
3925     
3926     if(this.containerScroll){
3927         Roo.dd.ScrollManager.register(this.el);
3928     }
3929     this.addEvents( {
3930          /**
3931          * @scope Roo.dd.DropTarget
3932          */
3933          
3934          /**
3935          * @event enter
3936          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3937          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3938          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3939          * 
3940          * IMPORTANT : it should set this.overClass and this.dropAllowed
3941          * 
3942          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3943          * @param {Event} e The event
3944          * @param {Object} data An object containing arbitrary data supplied by the drag source
3945          */
3946         "enter" : true,
3947         
3948          /**
3949          * @event over
3950          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3951          * This method will be called on every mouse movement while the drag source is over the drop target.
3952          * This default implementation simply returns the dropAllowed config value.
3953          * 
3954          * IMPORTANT : it should set this.dropAllowed
3955          * 
3956          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3957          * @param {Event} e The event
3958          * @param {Object} data An object containing arbitrary data supplied by the drag source
3959          
3960          */
3961         "over" : true,
3962         /**
3963          * @event out
3964          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3965          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3966          * overClass (if any) from the drop element.
3967          * 
3968          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3969          * @param {Event} e The event
3970          * @param {Object} data An object containing arbitrary data supplied by the drag source
3971          */
3972          "out" : true,
3973          
3974         /**
3975          * @event drop
3976          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3977          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3978          * implementation that does something to process the drop event and returns true so that the drag source's
3979          * repair action does not run.
3980          * 
3981          * IMPORTANT : it should set this.success
3982          * 
3983          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3984          * @param {Event} e The event
3985          * @param {Object} data An object containing arbitrary data supplied by the drag source
3986         */
3987          "drop" : true
3988     });
3989             
3990      
3991     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3992         this.el.dom, 
3993         this.ddGroup || this.group,
3994         {
3995             isTarget: true,
3996             listeners : listeners || {} 
3997            
3998         
3999         }
4000     );
4001
4002 };
4003
4004 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
4005     /**
4006      * @cfg {String} overClass
4007      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
4008      */
4009      /**
4010      * @cfg {String} ddGroup
4011      * The drag drop group to handle drop events for
4012      */
4013      
4014     /**
4015      * @cfg {String} dropAllowed
4016      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
4017      */
4018     dropAllowed : "x-dd-drop-ok",
4019     /**
4020      * @cfg {String} dropNotAllowed
4021      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
4022      */
4023     dropNotAllowed : "x-dd-drop-nodrop",
4024     /**
4025      * @cfg {boolean} success
4026      * set this after drop listener.. 
4027      */
4028     success : false,
4029     /**
4030      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4031      * if the drop point is valid for over/enter..
4032      */
4033     valid : false,
4034     // private
4035     isTarget : true,
4036
4037     // private
4038     isNotifyTarget : true,
4039     
4040     /**
4041      * @hide
4042      */
4043     notifyEnter : function(dd, e, data)
4044     {
4045         this.valid = true;
4046         this.fireEvent('enter', dd, e, data);
4047         if(this.overClass){
4048             this.el.addClass(this.overClass);
4049         }
4050         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4051             this.valid ? this.dropAllowed : this.dropNotAllowed
4052         );
4053     },
4054
4055     /**
4056      * @hide
4057      */
4058     notifyOver : function(dd, e, data)
4059     {
4060         this.valid = true;
4061         this.fireEvent('over', dd, e, data);
4062         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4063             this.valid ? this.dropAllowed : this.dropNotAllowed
4064         );
4065     },
4066
4067     /**
4068      * @hide
4069      */
4070     notifyOut : function(dd, e, data)
4071     {
4072         this.fireEvent('out', dd, e, data);
4073         if(this.overClass){
4074             this.el.removeClass(this.overClass);
4075         }
4076     },
4077
4078     /**
4079      * @hide
4080      */
4081     notifyDrop : function(dd, e, data)
4082     {
4083         this.success = false;
4084         this.fireEvent('drop', dd, e, data);
4085         return this.success;
4086     }
4087 });/*
4088  * Based on:
4089  * Ext JS Library 1.1.1
4090  * Copyright(c) 2006-2007, Ext JS, LLC.
4091  *
4092  * Originally Released Under LGPL - original licence link has changed is not relivant.
4093  *
4094  * Fork - LGPL
4095  * <script type="text/javascript">
4096  */
4097
4098
4099 /**
4100  * @class Roo.dd.DragZone
4101  * @extends Roo.dd.DragSource
4102  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4103  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4104  * @constructor
4105  * @param {String/HTMLElement/Element} el The container element
4106  * @param {Object} config
4107  */
4108 Roo.dd.DragZone = function(el, config){
4109     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4110     if(this.containerScroll){
4111         Roo.dd.ScrollManager.register(this.el);
4112     }
4113 };
4114
4115 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4116     /**
4117      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4118      * for auto scrolling during drag operations.
4119      */
4120     /**
4121      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4122      * method after a failed drop (defaults to "c3daf9" - light blue)
4123      */
4124
4125     /**
4126      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4127      * for a valid target to drag based on the mouse down. Override this method
4128      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4129      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4130      * @param {EventObject} e The mouse down event
4131      * @return {Object} The dragData
4132      */
4133     getDragData : function(e){
4134         return Roo.dd.Registry.getHandleFromEvent(e);
4135     },
4136     
4137     /**
4138      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4139      * this.dragData.ddel
4140      * @param {Number} x The x position of the click on the dragged object
4141      * @param {Number} y The y position of the click on the dragged object
4142      * @return {Boolean} true to continue the drag, false to cancel
4143      */
4144     onInitDrag : function(x, y){
4145         this.proxy.update(this.dragData.ddel.cloneNode(true));
4146         this.onStartDrag(x, y);
4147         return true;
4148     },
4149     
4150     /**
4151      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4152      */
4153     afterRepair : function(){
4154         if(Roo.enableFx){
4155             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4156         }
4157         this.dragging = false;
4158     },
4159
4160     /**
4161      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4162      * the XY of this.dragData.ddel
4163      * @param {EventObject} e The mouse up event
4164      * @return {Array} The xy location (e.g. [100, 200])
4165      */
4166     getRepairXY : function(e){
4167         return Roo.Element.fly(this.dragData.ddel).getXY();  
4168     }
4169 });/*
4170  * Based on:
4171  * Ext JS Library 1.1.1
4172  * Copyright(c) 2006-2007, Ext JS, LLC.
4173  *
4174  * Originally Released Under LGPL - original licence link has changed is not relivant.
4175  *
4176  * Fork - LGPL
4177  * <script type="text/javascript">
4178  */
4179 /**
4180  * @class Roo.dd.DropZone
4181  * @extends Roo.dd.DropTarget
4182  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4183  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4184  * @constructor
4185  * @param {String/HTMLElement/Element} el The container element
4186  * @param {Object} config
4187  */
4188 Roo.dd.DropZone = function(el, config){
4189     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4190 };
4191
4192 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4193     /**
4194      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4195      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4196      * provide your own custom lookup.
4197      * @param {Event} e The event
4198      * @return {Object} data The custom data
4199      */
4200     getTargetFromEvent : function(e){
4201         return Roo.dd.Registry.getTargetFromEvent(e);
4202     },
4203
4204     /**
4205      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4206      * that it has registered.  This method has no default implementation and should be overridden to provide
4207      * node-specific processing if necessary.
4208      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4209      * {@link #getTargetFromEvent} for this node)
4210      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4211      * @param {Event} e The event
4212      * @param {Object} data An object containing arbitrary data supplied by the drag source
4213      */
4214     onNodeEnter : function(n, dd, e, data){
4215         
4216     },
4217
4218     /**
4219      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4220      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4221      * overridden to provide the proper feedback.
4222      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4223      * {@link #getTargetFromEvent} for this node)
4224      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4225      * @param {Event} e The event
4226      * @param {Object} data An object containing arbitrary data supplied by the drag source
4227      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4228      * underlying {@link Roo.dd.StatusProxy} can be updated
4229      */
4230     onNodeOver : function(n, dd, e, data){
4231         return this.dropAllowed;
4232     },
4233
4234     /**
4235      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4236      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4237      * node-specific processing if necessary.
4238      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4239      * {@link #getTargetFromEvent} for this node)
4240      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4241      * @param {Event} e The event
4242      * @param {Object} data An object containing arbitrary data supplied by the drag source
4243      */
4244     onNodeOut : function(n, dd, e, data){
4245         
4246     },
4247
4248     /**
4249      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4250      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4251      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4252      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4253      * {@link #getTargetFromEvent} for this node)
4254      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4255      * @param {Event} e The event
4256      * @param {Object} data An object containing arbitrary data supplied by the drag source
4257      * @return {Boolean} True if the drop was valid, else false
4258      */
4259     onNodeDrop : function(n, dd, e, data){
4260         return false;
4261     },
4262
4263     /**
4264      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4265      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4266      * it should be overridden to provide the proper feedback if necessary.
4267      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4268      * @param {Event} e The event
4269      * @param {Object} data An object containing arbitrary data supplied by the drag source
4270      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4271      * underlying {@link Roo.dd.StatusProxy} can be updated
4272      */
4273     onContainerOver : function(dd, e, data){
4274         return this.dropNotAllowed;
4275     },
4276
4277     /**
4278      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4279      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4280      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4281      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4282      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4283      * @param {Event} e The event
4284      * @param {Object} data An object containing arbitrary data supplied by the drag source
4285      * @return {Boolean} True if the drop was valid, else false
4286      */
4287     onContainerDrop : function(dd, e, data){
4288         return false;
4289     },
4290
4291     /**
4292      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4293      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4294      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4295      * you should override this method and provide a custom implementation.
4296      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4297      * @param {Event} e The event
4298      * @param {Object} data An object containing arbitrary data supplied by the drag source
4299      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4300      * underlying {@link Roo.dd.StatusProxy} can be updated
4301      */
4302     notifyEnter : function(dd, e, data){
4303         return this.dropNotAllowed;
4304     },
4305
4306     /**
4307      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4308      * This method will be called on every mouse movement while the drag source is over the drop zone.
4309      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4310      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4311      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4312      * registered node, it will call {@link #onContainerOver}.
4313      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4314      * @param {Event} e The event
4315      * @param {Object} data An object containing arbitrary data supplied by the drag source
4316      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4317      * underlying {@link Roo.dd.StatusProxy} can be updated
4318      */
4319     notifyOver : function(dd, e, data){
4320         var n = this.getTargetFromEvent(e);
4321         if(!n){ // not over valid drop target
4322             if(this.lastOverNode){
4323                 this.onNodeOut(this.lastOverNode, dd, e, data);
4324                 this.lastOverNode = null;
4325             }
4326             return this.onContainerOver(dd, e, data);
4327         }
4328         if(this.lastOverNode != n){
4329             if(this.lastOverNode){
4330                 this.onNodeOut(this.lastOverNode, dd, e, data);
4331             }
4332             this.onNodeEnter(n, dd, e, data);
4333             this.lastOverNode = n;
4334         }
4335         return this.onNodeOver(n, dd, e, data);
4336     },
4337
4338     /**
4339      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4340      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4341      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4342      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4343      * @param {Event} e The event
4344      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4345      */
4346     notifyOut : function(dd, e, data){
4347         if(this.lastOverNode){
4348             this.onNodeOut(this.lastOverNode, dd, e, data);
4349             this.lastOverNode = null;
4350         }
4351     },
4352
4353     /**
4354      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4355      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4356      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4357      * otherwise it will call {@link #onContainerDrop}.
4358      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4359      * @param {Event} e The event
4360      * @param {Object} data An object containing arbitrary data supplied by the drag source
4361      * @return {Boolean} True if the drop was valid, else false
4362      */
4363     notifyDrop : function(dd, e, data){
4364         if(this.lastOverNode){
4365             this.onNodeOut(this.lastOverNode, dd, e, data);
4366             this.lastOverNode = null;
4367         }
4368         var n = this.getTargetFromEvent(e);
4369         return n ?
4370             this.onNodeDrop(n, dd, e, data) :
4371             this.onContainerDrop(dd, e, data);
4372     },
4373
4374     // private
4375     triggerCacheRefresh : function(){
4376         Roo.dd.DDM.refreshCache(this.groups);
4377     }  
4378 });/*
4379  * Based on:
4380  * Ext JS Library 1.1.1
4381  * Copyright(c) 2006-2007, Ext JS, LLC.
4382  *
4383  * Originally Released Under LGPL - original licence link has changed is not relivant.
4384  *
4385  * Fork - LGPL
4386  * <script type="text/javascript">
4387  */
4388
4389
4390 /**
4391  * @class Roo.data.SortTypes
4392  * @singleton
4393  * Defines the default sorting (casting?) comparison functions used when sorting data.
4394  */
4395 Roo.data.SortTypes = {
4396     /**
4397      * Default sort that does nothing
4398      * @param {Mixed} s The value being converted
4399      * @return {Mixed} The comparison value
4400      */
4401     none : function(s){
4402         return s;
4403     },
4404     
4405     /**
4406      * The regular expression used to strip tags
4407      * @type {RegExp}
4408      * @property
4409      */
4410     stripTagsRE : /<\/?[^>]+>/gi,
4411     
4412     /**
4413      * Strips all HTML tags to sort on text only
4414      * @param {Mixed} s The value being converted
4415      * @return {String} The comparison value
4416      */
4417     asText : function(s){
4418         return String(s).replace(this.stripTagsRE, "");
4419     },
4420     
4421     /**
4422      * Strips all HTML tags to sort on text only - Case insensitive
4423      * @param {Mixed} s The value being converted
4424      * @return {String} The comparison value
4425      */
4426     asUCText : function(s){
4427         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4428     },
4429     
4430     /**
4431      * Case insensitive string
4432      * @param {Mixed} s The value being converted
4433      * @return {String} The comparison value
4434      */
4435     asUCString : function(s) {
4436         return String(s).toUpperCase();
4437     },
4438     
4439     /**
4440      * Date sorting
4441      * @param {Mixed} s The value being converted
4442      * @return {Number} The comparison value
4443      */
4444     asDate : function(s) {
4445         if(!s){
4446             return 0;
4447         }
4448         if(s instanceof Date){
4449             return s.getTime();
4450         }
4451         return Date.parse(String(s));
4452     },
4453     
4454     /**
4455      * Float sorting
4456      * @param {Mixed} s The value being converted
4457      * @return {Float} The comparison value
4458      */
4459     asFloat : function(s) {
4460         var val = parseFloat(String(s).replace(/,/g, ""));
4461         if(isNaN(val)) val = 0;
4462         return val;
4463     },
4464     
4465     /**
4466      * Integer sorting
4467      * @param {Mixed} s The value being converted
4468      * @return {Number} The comparison value
4469      */
4470     asInt : function(s) {
4471         var val = parseInt(String(s).replace(/,/g, ""));
4472         if(isNaN(val)) val = 0;
4473         return val;
4474     }
4475 };/*
4476  * Based on:
4477  * Ext JS Library 1.1.1
4478  * Copyright(c) 2006-2007, Ext JS, LLC.
4479  *
4480  * Originally Released Under LGPL - original licence link has changed is not relivant.
4481  *
4482  * Fork - LGPL
4483  * <script type="text/javascript">
4484  */
4485
4486 /**
4487 * @class Roo.data.Record
4488  * Instances of this class encapsulate both record <em>definition</em> information, and record
4489  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4490  * to access Records cached in an {@link Roo.data.Store} object.<br>
4491  * <p>
4492  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4493  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4494  * objects.<br>
4495  * <p>
4496  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4497  * @constructor
4498  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4499  * {@link #create}. The parameters are the same.
4500  * @param {Array} data An associative Array of data values keyed by the field name.
4501  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4502  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4503  * not specified an integer id is generated.
4504  */
4505 Roo.data.Record = function(data, id){
4506     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4507     this.data = data;
4508 };
4509
4510 /**
4511  * Generate a constructor for a specific record layout.
4512  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4513  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4514  * Each field definition object may contain the following properties: <ul>
4515  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
4516  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4517  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4518  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4519  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4520  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4521  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4522  * this may be omitted.</p></li>
4523  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4524  * <ul><li>auto (Default, implies no conversion)</li>
4525  * <li>string</li>
4526  * <li>int</li>
4527  * <li>float</li>
4528  * <li>boolean</li>
4529  * <li>date</li></ul></p></li>
4530  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4531  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4532  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4533  * by the Reader into an object that will be stored in the Record. It is passed the
4534  * following parameters:<ul>
4535  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4536  * </ul></p></li>
4537  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4538  * </ul>
4539  * <br>usage:<br><pre><code>
4540 var TopicRecord = Roo.data.Record.create(
4541     {name: 'title', mapping: 'topic_title'},
4542     {name: 'author', mapping: 'username'},
4543     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4544     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4545     {name: 'lastPoster', mapping: 'user2'},
4546     {name: 'excerpt', mapping: 'post_text'}
4547 );
4548
4549 var myNewRecord = new TopicRecord({
4550     title: 'Do my job please',
4551     author: 'noobie',
4552     totalPosts: 1,
4553     lastPost: new Date(),
4554     lastPoster: 'Animal',
4555     excerpt: 'No way dude!'
4556 });
4557 myStore.add(myNewRecord);
4558 </code></pre>
4559  * @method create
4560  * @static
4561  */
4562 Roo.data.Record.create = function(o){
4563     var f = function(){
4564         f.superclass.constructor.apply(this, arguments);
4565     };
4566     Roo.extend(f, Roo.data.Record);
4567     var p = f.prototype;
4568     p.fields = new Roo.util.MixedCollection(false, function(field){
4569         return field.name;
4570     });
4571     for(var i = 0, len = o.length; i < len; i++){
4572         p.fields.add(new Roo.data.Field(o[i]));
4573     }
4574     f.getField = function(name){
4575         return p.fields.get(name);  
4576     };
4577     return f;
4578 };
4579
4580 Roo.data.Record.AUTO_ID = 1000;
4581 Roo.data.Record.EDIT = 'edit';
4582 Roo.data.Record.REJECT = 'reject';
4583 Roo.data.Record.COMMIT = 'commit';
4584
4585 Roo.data.Record.prototype = {
4586     /**
4587      * Readonly flag - true if this record has been modified.
4588      * @type Boolean
4589      */
4590     dirty : false,
4591     editing : false,
4592     error: null,
4593     modified: null,
4594
4595     // private
4596     join : function(store){
4597         this.store = store;
4598     },
4599
4600     /**
4601      * Set the named field to the specified value.
4602      * @param {String} name The name of the field to set.
4603      * @param {Object} value The value to set the field to.
4604      */
4605     set : function(name, value){
4606         if(this.data[name] == value){
4607             return;
4608         }
4609         this.dirty = true;
4610         if(!this.modified){
4611             this.modified = {};
4612         }
4613         if(typeof this.modified[name] == 'undefined'){
4614             this.modified[name] = this.data[name];
4615         }
4616         this.data[name] = value;
4617         if(!this.editing && this.store){
4618             this.store.afterEdit(this);
4619         }       
4620     },
4621
4622     /**
4623      * Get the value of the named field.
4624      * @param {String} name The name of the field to get the value of.
4625      * @return {Object} The value of the field.
4626      */
4627     get : function(name){
4628         return this.data[name]; 
4629     },
4630
4631     // private
4632     beginEdit : function(){
4633         this.editing = true;
4634         this.modified = {}; 
4635     },
4636
4637     // private
4638     cancelEdit : function(){
4639         this.editing = false;
4640         delete this.modified;
4641     },
4642
4643     // private
4644     endEdit : function(){
4645         this.editing = false;
4646         if(this.dirty && this.store){
4647             this.store.afterEdit(this);
4648         }
4649     },
4650
4651     /**
4652      * Usually called by the {@link Roo.data.Store} which owns the Record.
4653      * Rejects all changes made to the Record since either creation, or the last commit operation.
4654      * Modified fields are reverted to their original values.
4655      * <p>
4656      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4657      * of reject operations.
4658      */
4659     reject : function(){
4660         var m = this.modified;
4661         for(var n in m){
4662             if(typeof m[n] != "function"){
4663                 this.data[n] = m[n];
4664             }
4665         }
4666         this.dirty = false;
4667         delete this.modified;
4668         this.editing = false;
4669         if(this.store){
4670             this.store.afterReject(this);
4671         }
4672     },
4673
4674     /**
4675      * Usually called by the {@link Roo.data.Store} which owns the Record.
4676      * Commits all changes made to the Record since either creation, or the last commit operation.
4677      * <p>
4678      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4679      * of commit operations.
4680      */
4681     commit : function(){
4682         this.dirty = false;
4683         delete this.modified;
4684         this.editing = false;
4685         if(this.store){
4686             this.store.afterCommit(this);
4687         }
4688     },
4689
4690     // private
4691     hasError : function(){
4692         return this.error != null;
4693     },
4694
4695     // private
4696     clearError : function(){
4697         this.error = null;
4698     },
4699
4700     /**
4701      * Creates a copy of this record.
4702      * @param {String} id (optional) A new record id if you don't want to use this record's id
4703      * @return {Record}
4704      */
4705     copy : function(newId) {
4706         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4707     }
4708 };/*
4709  * Based on:
4710  * Ext JS Library 1.1.1
4711  * Copyright(c) 2006-2007, Ext JS, LLC.
4712  *
4713  * Originally Released Under LGPL - original licence link has changed is not relivant.
4714  *
4715  * Fork - LGPL
4716  * <script type="text/javascript">
4717  */
4718
4719
4720
4721 /**
4722  * @class Roo.data.Store
4723  * @extends Roo.util.Observable
4724  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4725  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4726  * <p>
4727  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
4728  * has no knowledge of the format of the data returned by the Proxy.<br>
4729  * <p>
4730  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4731  * instances from the data object. These records are cached and made available through accessor functions.
4732  * @constructor
4733  * Creates a new Store.
4734  * @param {Object} config A config object containing the objects needed for the Store to access data,
4735  * and read the data into Records.
4736  */
4737 Roo.data.Store = function(config){
4738     this.data = new Roo.util.MixedCollection(false);
4739     this.data.getKey = function(o){
4740         return o.id;
4741     };
4742     this.baseParams = {};
4743     // private
4744     this.paramNames = {
4745         "start" : "start",
4746         "limit" : "limit",
4747         "sort" : "sort",
4748         "dir" : "dir",
4749         "multisort" : "_multisort"
4750     };
4751
4752     if(config && config.data){
4753         this.inlineData = config.data;
4754         delete config.data;
4755     }
4756
4757     Roo.apply(this, config);
4758     
4759     if(this.reader){ // reader passed
4760         this.reader = Roo.factory(this.reader, Roo.data);
4761         this.reader.xmodule = this.xmodule || false;
4762         if(!this.recordType){
4763             this.recordType = this.reader.recordType;
4764         }
4765         if(this.reader.onMetaChange){
4766             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4767         }
4768     }
4769
4770     if(this.recordType){
4771         this.fields = this.recordType.prototype.fields;
4772     }
4773     this.modified = [];
4774
4775     this.addEvents({
4776         /**
4777          * @event datachanged
4778          * Fires when the data cache has changed, and a widget which is using this Store
4779          * as a Record cache should refresh its view.
4780          * @param {Store} this
4781          */
4782         datachanged : true,
4783         /**
4784          * @event metachange
4785          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4786          * @param {Store} this
4787          * @param {Object} meta The JSON metadata
4788          */
4789         metachange : true,
4790         /**
4791          * @event add
4792          * Fires when Records have been added to the Store
4793          * @param {Store} this
4794          * @param {Roo.data.Record[]} records The array of Records added
4795          * @param {Number} index The index at which the record(s) were added
4796          */
4797         add : true,
4798         /**
4799          * @event remove
4800          * Fires when a Record has been removed from the Store
4801          * @param {Store} this
4802          * @param {Roo.data.Record} record The Record that was removed
4803          * @param {Number} index The index at which the record was removed
4804          */
4805         remove : true,
4806         /**
4807          * @event update
4808          * Fires when a Record has been updated
4809          * @param {Store} this
4810          * @param {Roo.data.Record} record The Record that was updated
4811          * @param {String} operation The update operation being performed.  Value may be one of:
4812          * <pre><code>
4813  Roo.data.Record.EDIT
4814  Roo.data.Record.REJECT
4815  Roo.data.Record.COMMIT
4816          * </code></pre>
4817          */
4818         update : true,
4819         /**
4820          * @event clear
4821          * Fires when the data cache has been cleared.
4822          * @param {Store} this
4823          */
4824         clear : true,
4825         /**
4826          * @event beforeload
4827          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4828          * the load action will be canceled.
4829          * @param {Store} this
4830          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4831          */
4832         beforeload : true,
4833         /**
4834          * @event beforeloadadd
4835          * Fires after a new set of Records has been loaded.
4836          * @param {Store} this
4837          * @param {Roo.data.Record[]} records The Records that were loaded
4838          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4839          */
4840         beforeloadadd : true,
4841         /**
4842          * @event load
4843          * Fires after a new set of Records has been loaded, before they are added to the store.
4844          * @param {Store} this
4845          * @param {Roo.data.Record[]} records The Records that were loaded
4846          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4847          * @params {Object} return from reader
4848          */
4849         load : true,
4850         /**
4851          * @event loadexception
4852          * Fires if an exception occurs in the Proxy during loading.
4853          * Called with the signature of the Proxy's "loadexception" event.
4854          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4855          * 
4856          * @param {Proxy} 
4857          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4858          * @param {Object} load options 
4859          * @param {Object} jsonData from your request (normally this contains the Exception)
4860          */
4861         loadexception : true
4862     });
4863     
4864     if(this.proxy){
4865         this.proxy = Roo.factory(this.proxy, Roo.data);
4866         this.proxy.xmodule = this.xmodule || false;
4867         this.relayEvents(this.proxy,  ["loadexception"]);
4868     }
4869     this.sortToggle = {};
4870     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4871
4872     Roo.data.Store.superclass.constructor.call(this);
4873
4874     if(this.inlineData){
4875         this.loadData(this.inlineData);
4876         delete this.inlineData;
4877     }
4878 };
4879
4880 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4881      /**
4882     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4883     * without a remote query - used by combo/forms at present.
4884     */
4885     
4886     /**
4887     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4888     */
4889     /**
4890     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4891     */
4892     /**
4893     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4894     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4895     */
4896     /**
4897     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4898     * on any HTTP request
4899     */
4900     /**
4901     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4902     */
4903     /**
4904     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4905     */
4906     multiSort: false,
4907     /**
4908     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4909     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4910     */
4911     remoteSort : false,
4912
4913     /**
4914     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4915      * loaded or when a record is removed. (defaults to false).
4916     */
4917     pruneModifiedRecords : false,
4918
4919     // private
4920     lastOptions : null,
4921
4922     /**
4923      * Add Records to the Store and fires the add event.
4924      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4925      */
4926     add : function(records){
4927         records = [].concat(records);
4928         for(var i = 0, len = records.length; i < len; i++){
4929             records[i].join(this);
4930         }
4931         var index = this.data.length;
4932         this.data.addAll(records);
4933         this.fireEvent("add", this, records, index);
4934     },
4935
4936     /**
4937      * Remove a Record from the Store and fires the remove event.
4938      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4939      */
4940     remove : function(record){
4941         var index = this.data.indexOf(record);
4942         this.data.removeAt(index);
4943         if(this.pruneModifiedRecords){
4944             this.modified.remove(record);
4945         }
4946         this.fireEvent("remove", this, record, index);
4947     },
4948
4949     /**
4950      * Remove all Records from the Store and fires the clear event.
4951      */
4952     removeAll : function(){
4953         this.data.clear();
4954         if(this.pruneModifiedRecords){
4955             this.modified = [];
4956         }
4957         this.fireEvent("clear", this);
4958     },
4959
4960     /**
4961      * Inserts Records to the Store at the given index and fires the add event.
4962      * @param {Number} index The start index at which to insert the passed Records.
4963      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4964      */
4965     insert : function(index, records){
4966         records = [].concat(records);
4967         for(var i = 0, len = records.length; i < len; i++){
4968             this.data.insert(index, records[i]);
4969             records[i].join(this);
4970         }
4971         this.fireEvent("add", this, records, index);
4972     },
4973
4974     /**
4975      * Get the index within the cache of the passed Record.
4976      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4977      * @return {Number} The index of the passed Record. Returns -1 if not found.
4978      */
4979     indexOf : function(record){
4980         return this.data.indexOf(record);
4981     },
4982
4983     /**
4984      * Get the index within the cache of the Record with the passed id.
4985      * @param {String} id The id of the Record to find.
4986      * @return {Number} The index of the Record. Returns -1 if not found.
4987      */
4988     indexOfId : function(id){
4989         return this.data.indexOfKey(id);
4990     },
4991
4992     /**
4993      * Get the Record with the specified id.
4994      * @param {String} id The id of the Record to find.
4995      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4996      */
4997     getById : function(id){
4998         return this.data.key(id);
4999     },
5000
5001     /**
5002      * Get the Record at the specified index.
5003      * @param {Number} index The index of the Record to find.
5004      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
5005      */
5006     getAt : function(index){
5007         return this.data.itemAt(index);
5008     },
5009
5010     /**
5011      * Returns a range of Records between specified indices.
5012      * @param {Number} startIndex (optional) The starting index (defaults to 0)
5013      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
5014      * @return {Roo.data.Record[]} An array of Records
5015      */
5016     getRange : function(start, end){
5017         return this.data.getRange(start, end);
5018     },
5019
5020     // private
5021     storeOptions : function(o){
5022         o = Roo.apply({}, o);
5023         delete o.callback;
5024         delete o.scope;
5025         this.lastOptions = o;
5026     },
5027
5028     /**
5029      * Loads the Record cache from the configured Proxy using the configured Reader.
5030      * <p>
5031      * If using remote paging, then the first load call must specify the <em>start</em>
5032      * and <em>limit</em> properties in the options.params property to establish the initial
5033      * position within the dataset, and the number of Records to cache on each read from the Proxy.
5034      * <p>
5035      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5036      * and this call will return before the new data has been loaded. Perform any post-processing
5037      * in a callback function, or in a "load" event handler.</strong>
5038      * <p>
5039      * @param {Object} options An object containing properties which control loading options:<ul>
5040      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5041      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5042      * passed the following arguments:<ul>
5043      * <li>r : Roo.data.Record[]</li>
5044      * <li>options: Options object from the load call</li>
5045      * <li>success: Boolean success indicator</li></ul></li>
5046      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5047      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5048      * </ul>
5049      */
5050     load : function(options){
5051         options = options || {};
5052         if(this.fireEvent("beforeload", this, options) !== false){
5053             this.storeOptions(options);
5054             var p = Roo.apply(options.params || {}, this.baseParams);
5055             // if meta was not loaded from remote source.. try requesting it.
5056             if (!this.reader.metaFromRemote) {
5057                 p._requestMeta = 1;
5058             }
5059             if(this.sortInfo && this.remoteSort){
5060                 var pn = this.paramNames;
5061                 p[pn["sort"]] = this.sortInfo.field;
5062                 p[pn["dir"]] = this.sortInfo.direction;
5063             }
5064             if (this.multiSort) {
5065                 var pn = this.paramNames;
5066                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5067             }
5068             
5069             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5070         }
5071     },
5072
5073     /**
5074      * Reloads the Record cache from the configured Proxy using the configured Reader and
5075      * the options from the last load operation performed.
5076      * @param {Object} options (optional) An object containing properties which may override the options
5077      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5078      * the most recently used options are reused).
5079      */
5080     reload : function(options){
5081         this.load(Roo.applyIf(options||{}, this.lastOptions));
5082     },
5083
5084     // private
5085     // Called as a callback by the Reader during a load operation.
5086     loadRecords : function(o, options, success){
5087         if(!o || success === false){
5088             if(success !== false){
5089                 this.fireEvent("load", this, [], options, o);
5090             }
5091             if(options.callback){
5092                 options.callback.call(options.scope || this, [], options, false);
5093             }
5094             return;
5095         }
5096         // if data returned failure - throw an exception.
5097         if (o.success === false) {
5098             // show a message if no listener is registered.
5099             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
5100                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
5101             }
5102             // loadmask wil be hooked into this..
5103             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
5104             return;
5105         }
5106         var r = o.records, t = o.totalRecords || r.length;
5107         
5108         this.fireEvent("beforeloadadd", this, r, options, o);
5109         
5110         if(!options || options.add !== true){
5111             if(this.pruneModifiedRecords){
5112                 this.modified = [];
5113             }
5114             for(var i = 0, len = r.length; i < len; i++){
5115                 r[i].join(this);
5116             }
5117             if(this.snapshot){
5118                 this.data = this.snapshot;
5119                 delete this.snapshot;
5120             }
5121             this.data.clear();
5122             this.data.addAll(r);
5123             this.totalLength = t;
5124             this.applySort();
5125             this.fireEvent("datachanged", this);
5126         }else{
5127             this.totalLength = Math.max(t, this.data.length+r.length);
5128             this.add(r);
5129         }
5130         this.fireEvent("load", this, r, options, o);
5131         if(options.callback){
5132             options.callback.call(options.scope || this, r, options, true);
5133         }
5134     },
5135
5136
5137     /**
5138      * Loads data from a passed data block. A Reader which understands the format of the data
5139      * must have been configured in the constructor.
5140      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5141      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5142      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5143      */
5144     loadData : function(o, append){
5145         var r = this.reader.readRecords(o);
5146         this.loadRecords(r, {add: append}, true);
5147     },
5148
5149     /**
5150      * Gets the number of cached records.
5151      * <p>
5152      * <em>If using paging, this may not be the total size of the dataset. If the data object
5153      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5154      * the data set size</em>
5155      */
5156     getCount : function(){
5157         return this.data.length || 0;
5158     },
5159
5160     /**
5161      * Gets the total number of records in the dataset as returned by the server.
5162      * <p>
5163      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5164      * the dataset size</em>
5165      */
5166     getTotalCount : function(){
5167         return this.totalLength || 0;
5168     },
5169
5170     /**
5171      * Returns the sort state of the Store as an object with two properties:
5172      * <pre><code>
5173  field {String} The name of the field by which the Records are sorted
5174  direction {String} The sort order, "ASC" or "DESC"
5175      * </code></pre>
5176      */
5177     getSortState : function(){
5178         return this.sortInfo;
5179     },
5180
5181     // private
5182     applySort : function(){
5183         if(this.sortInfo && !this.remoteSort){
5184             var s = this.sortInfo, f = s.field;
5185             var st = this.fields.get(f).sortType;
5186             var fn = function(r1, r2){
5187                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5188                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5189             };
5190             this.data.sort(s.direction, fn);
5191             if(this.snapshot && this.snapshot != this.data){
5192                 this.snapshot.sort(s.direction, fn);
5193             }
5194         }
5195     },
5196
5197     /**
5198      * Sets the default sort column and order to be used by the next load operation.
5199      * @param {String} fieldName The name of the field to sort by.
5200      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5201      */
5202     setDefaultSort : function(field, dir){
5203         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5204     },
5205
5206     /**
5207      * Sort the Records.
5208      * If remote sorting is used, the sort is performed on the server, and the cache is
5209      * reloaded. If local sorting is used, the cache is sorted internally.
5210      * @param {String} fieldName The name of the field to sort by.
5211      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5212      */
5213     sort : function(fieldName, dir){
5214         var f = this.fields.get(fieldName);
5215         if(!dir){
5216             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5217             
5218             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5219                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5220             }else{
5221                 dir = f.sortDir;
5222             }
5223         }
5224         this.sortToggle[f.name] = dir;
5225         this.sortInfo = {field: f.name, direction: dir};
5226         if(!this.remoteSort){
5227             this.applySort();
5228             this.fireEvent("datachanged", this);
5229         }else{
5230             this.load(this.lastOptions);
5231         }
5232     },
5233
5234     /**
5235      * Calls the specified function for each of the Records in the cache.
5236      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5237      * Returning <em>false</em> aborts and exits the iteration.
5238      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5239      */
5240     each : function(fn, scope){
5241         this.data.each(fn, scope);
5242     },
5243
5244     /**
5245      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5246      * (e.g., during paging).
5247      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5248      */
5249     getModifiedRecords : function(){
5250         return this.modified;
5251     },
5252
5253     // private
5254     createFilterFn : function(property, value, anyMatch){
5255         if(!value.exec){ // not a regex
5256             value = String(value);
5257             if(value.length == 0){
5258                 return false;
5259             }
5260             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5261         }
5262         return function(r){
5263             return value.test(r.data[property]);
5264         };
5265     },
5266
5267     /**
5268      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5269      * @param {String} property A field on your records
5270      * @param {Number} start The record index to start at (defaults to 0)
5271      * @param {Number} end The last record index to include (defaults to length - 1)
5272      * @return {Number} The sum
5273      */
5274     sum : function(property, start, end){
5275         var rs = this.data.items, v = 0;
5276         start = start || 0;
5277         end = (end || end === 0) ? end : rs.length-1;
5278
5279         for(var i = start; i <= end; i++){
5280             v += (rs[i].data[property] || 0);
5281         }
5282         return v;
5283     },
5284
5285     /**
5286      * Filter the records by a specified property.
5287      * @param {String} field A field on your records
5288      * @param {String/RegExp} value Either a string that the field
5289      * should start with or a RegExp to test against the field
5290      * @param {Boolean} anyMatch True to match any part not just the beginning
5291      */
5292     filter : function(property, value, anyMatch){
5293         var fn = this.createFilterFn(property, value, anyMatch);
5294         return fn ? this.filterBy(fn) : this.clearFilter();
5295     },
5296
5297     /**
5298      * Filter by a function. The specified function will be called with each
5299      * record in this data source. If the function returns true the record is included,
5300      * otherwise it is filtered.
5301      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5302      * @param {Object} scope (optional) The scope of the function (defaults to this)
5303      */
5304     filterBy : function(fn, scope){
5305         this.snapshot = this.snapshot || this.data;
5306         this.data = this.queryBy(fn, scope||this);
5307         this.fireEvent("datachanged", this);
5308     },
5309
5310     /**
5311      * Query the records by a specified property.
5312      * @param {String} field A field on your records
5313      * @param {String/RegExp} value Either a string that the field
5314      * should start with or a RegExp to test against the field
5315      * @param {Boolean} anyMatch True to match any part not just the beginning
5316      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5317      */
5318     query : function(property, value, anyMatch){
5319         var fn = this.createFilterFn(property, value, anyMatch);
5320         return fn ? this.queryBy(fn) : this.data.clone();
5321     },
5322
5323     /**
5324      * Query by a function. The specified function will be called with each
5325      * record in this data source. If the function returns true the record is included
5326      * in the results.
5327      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5328      * @param {Object} scope (optional) The scope of the function (defaults to this)
5329       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5330      **/
5331     queryBy : function(fn, scope){
5332         var data = this.snapshot || this.data;
5333         return data.filterBy(fn, scope||this);
5334     },
5335
5336     /**
5337      * Collects unique values for a particular dataIndex from this store.
5338      * @param {String} dataIndex The property to collect
5339      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5340      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5341      * @return {Array} An array of the unique values
5342      **/
5343     collect : function(dataIndex, allowNull, bypassFilter){
5344         var d = (bypassFilter === true && this.snapshot) ?
5345                 this.snapshot.items : this.data.items;
5346         var v, sv, r = [], l = {};
5347         for(var i = 0, len = d.length; i < len; i++){
5348             v = d[i].data[dataIndex];
5349             sv = String(v);
5350             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5351                 l[sv] = true;
5352                 r[r.length] = v;
5353             }
5354         }
5355         return r;
5356     },
5357
5358     /**
5359      * Revert to a view of the Record cache with no filtering applied.
5360      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5361      */
5362     clearFilter : function(suppressEvent){
5363         if(this.snapshot && this.snapshot != this.data){
5364             this.data = this.snapshot;
5365             delete this.snapshot;
5366             if(suppressEvent !== true){
5367                 this.fireEvent("datachanged", this);
5368             }
5369         }
5370     },
5371
5372     // private
5373     afterEdit : function(record){
5374         if(this.modified.indexOf(record) == -1){
5375             this.modified.push(record);
5376         }
5377         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5378     },
5379     
5380     // private
5381     afterReject : function(record){
5382         this.modified.remove(record);
5383         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5384     },
5385
5386     // private
5387     afterCommit : function(record){
5388         this.modified.remove(record);
5389         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5390     },
5391
5392     /**
5393      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5394      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5395      */
5396     commitChanges : function(){
5397         var m = this.modified.slice(0);
5398         this.modified = [];
5399         for(var i = 0, len = m.length; i < len; i++){
5400             m[i].commit();
5401         }
5402     },
5403
5404     /**
5405      * Cancel outstanding changes on all changed records.
5406      */
5407     rejectChanges : function(){
5408         var m = this.modified.slice(0);
5409         this.modified = [];
5410         for(var i = 0, len = m.length; i < len; i++){
5411             m[i].reject();
5412         }
5413     },
5414
5415     onMetaChange : function(meta, rtype, o){
5416         this.recordType = rtype;
5417         this.fields = rtype.prototype.fields;
5418         delete this.snapshot;
5419         this.sortInfo = meta.sortInfo || this.sortInfo;
5420         this.modified = [];
5421         this.fireEvent('metachange', this, this.reader.meta);
5422     },
5423     
5424     moveIndex : function(data, type)
5425     {
5426         var index = this.indexOf(data);
5427         
5428         var newIndex = index + type;
5429         
5430         this.remove(data);
5431         
5432         this.insert(newIndex, data);
5433         
5434     }
5435 });/*
5436  * Based on:
5437  * Ext JS Library 1.1.1
5438  * Copyright(c) 2006-2007, Ext JS, LLC.
5439  *
5440  * Originally Released Under LGPL - original licence link has changed is not relivant.
5441  *
5442  * Fork - LGPL
5443  * <script type="text/javascript">
5444  */
5445
5446 /**
5447  * @class Roo.data.SimpleStore
5448  * @extends Roo.data.Store
5449  * Small helper class to make creating Stores from Array data easier.
5450  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5451  * @cfg {Array} fields An array of field definition objects, or field name strings.
5452  * @cfg {Array} data The multi-dimensional array of data
5453  * @constructor
5454  * @param {Object} config
5455  */
5456 Roo.data.SimpleStore = function(config){
5457     Roo.data.SimpleStore.superclass.constructor.call(this, {
5458         isLocal : true,
5459         reader: new Roo.data.ArrayReader({
5460                 id: config.id
5461             },
5462             Roo.data.Record.create(config.fields)
5463         ),
5464         proxy : new Roo.data.MemoryProxy(config.data)
5465     });
5466     this.load();
5467 };
5468 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5469  * Based on:
5470  * Ext JS Library 1.1.1
5471  * Copyright(c) 2006-2007, Ext JS, LLC.
5472  *
5473  * Originally Released Under LGPL - original licence link has changed is not relivant.
5474  *
5475  * Fork - LGPL
5476  * <script type="text/javascript">
5477  */
5478
5479 /**
5480 /**
5481  * @extends Roo.data.Store
5482  * @class Roo.data.JsonStore
5483  * Small helper class to make creating Stores for JSON data easier. <br/>
5484 <pre><code>
5485 var store = new Roo.data.JsonStore({
5486     url: 'get-images.php',
5487     root: 'images',
5488     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5489 });
5490 </code></pre>
5491  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5492  * JsonReader and HttpProxy (unless inline data is provided).</b>
5493  * @cfg {Array} fields An array of field definition objects, or field name strings.
5494  * @constructor
5495  * @param {Object} config
5496  */
5497 Roo.data.JsonStore = function(c){
5498     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5499         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5500         reader: new Roo.data.JsonReader(c, c.fields)
5501     }));
5502 };
5503 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5504  * Based on:
5505  * Ext JS Library 1.1.1
5506  * Copyright(c) 2006-2007, Ext JS, LLC.
5507  *
5508  * Originally Released Under LGPL - original licence link has changed is not relivant.
5509  *
5510  * Fork - LGPL
5511  * <script type="text/javascript">
5512  */
5513
5514  
5515 Roo.data.Field = function(config){
5516     if(typeof config == "string"){
5517         config = {name: config};
5518     }
5519     Roo.apply(this, config);
5520     
5521     if(!this.type){
5522         this.type = "auto";
5523     }
5524     
5525     var st = Roo.data.SortTypes;
5526     // named sortTypes are supported, here we look them up
5527     if(typeof this.sortType == "string"){
5528         this.sortType = st[this.sortType];
5529     }
5530     
5531     // set default sortType for strings and dates
5532     if(!this.sortType){
5533         switch(this.type){
5534             case "string":
5535                 this.sortType = st.asUCString;
5536                 break;
5537             case "date":
5538                 this.sortType = st.asDate;
5539                 break;
5540             default:
5541                 this.sortType = st.none;
5542         }
5543     }
5544
5545     // define once
5546     var stripRe = /[\$,%]/g;
5547
5548     // prebuilt conversion function for this field, instead of
5549     // switching every time we're reading a value
5550     if(!this.convert){
5551         var cv, dateFormat = this.dateFormat;
5552         switch(this.type){
5553             case "":
5554             case "auto":
5555             case undefined:
5556                 cv = function(v){ return v; };
5557                 break;
5558             case "string":
5559                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5560                 break;
5561             case "int":
5562                 cv = function(v){
5563                     return v !== undefined && v !== null && v !== '' ?
5564                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5565                     };
5566                 break;
5567             case "float":
5568                 cv = function(v){
5569                     return v !== undefined && v !== null && v !== '' ?
5570                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5571                     };
5572                 break;
5573             case "bool":
5574             case "boolean":
5575                 cv = function(v){ return v === true || v === "true" || v == 1; };
5576                 break;
5577             case "date":
5578                 cv = function(v){
5579                     if(!v){
5580                         return '';
5581                     }
5582                     if(v instanceof Date){
5583                         return v;
5584                     }
5585                     if(dateFormat){
5586                         if(dateFormat == "timestamp"){
5587                             return new Date(v*1000);
5588                         }
5589                         return Date.parseDate(v, dateFormat);
5590                     }
5591                     var parsed = Date.parse(v);
5592                     return parsed ? new Date(parsed) : null;
5593                 };
5594              break;
5595             
5596         }
5597         this.convert = cv;
5598     }
5599 };
5600
5601 Roo.data.Field.prototype = {
5602     dateFormat: null,
5603     defaultValue: "",
5604     mapping: null,
5605     sortType : null,
5606     sortDir : "ASC"
5607 };/*
5608  * Based on:
5609  * Ext JS Library 1.1.1
5610  * Copyright(c) 2006-2007, Ext JS, LLC.
5611  *
5612  * Originally Released Under LGPL - original licence link has changed is not relivant.
5613  *
5614  * Fork - LGPL
5615  * <script type="text/javascript">
5616  */
5617  
5618 // Base class for reading structured data from a data source.  This class is intended to be
5619 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5620
5621 /**
5622  * @class Roo.data.DataReader
5623  * Base class for reading structured data from a data source.  This class is intended to be
5624  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5625  */
5626
5627 Roo.data.DataReader = function(meta, recordType){
5628     
5629     this.meta = meta;
5630     
5631     this.recordType = recordType instanceof Array ? 
5632         Roo.data.Record.create(recordType) : recordType;
5633 };
5634
5635 Roo.data.DataReader.prototype = {
5636      /**
5637      * Create an empty record
5638      * @param {Object} data (optional) - overlay some values
5639      * @return {Roo.data.Record} record created.
5640      */
5641     newRow :  function(d) {
5642         var da =  {};
5643         this.recordType.prototype.fields.each(function(c) {
5644             switch( c.type) {
5645                 case 'int' : da[c.name] = 0; break;
5646                 case 'date' : da[c.name] = new Date(); break;
5647                 case 'float' : da[c.name] = 0.0; break;
5648                 case 'boolean' : da[c.name] = false; break;
5649                 default : da[c.name] = ""; break;
5650             }
5651             
5652         });
5653         return new this.recordType(Roo.apply(da, d));
5654     }
5655     
5656 };/*
5657  * Based on:
5658  * Ext JS Library 1.1.1
5659  * Copyright(c) 2006-2007, Ext JS, LLC.
5660  *
5661  * Originally Released Under LGPL - original licence link has changed is not relivant.
5662  *
5663  * Fork - LGPL
5664  * <script type="text/javascript">
5665  */
5666
5667 /**
5668  * @class Roo.data.DataProxy
5669  * @extends Roo.data.Observable
5670  * This class is an abstract base class for implementations which provide retrieval of
5671  * unformatted data objects.<br>
5672  * <p>
5673  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5674  * (of the appropriate type which knows how to parse the data object) to provide a block of
5675  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5676  * <p>
5677  * Custom implementations must implement the load method as described in
5678  * {@link Roo.data.HttpProxy#load}.
5679  */
5680 Roo.data.DataProxy = function(){
5681     this.addEvents({
5682         /**
5683          * @event beforeload
5684          * Fires before a network request is made to retrieve a data object.
5685          * @param {Object} This DataProxy object.
5686          * @param {Object} params The params parameter to the load function.
5687          */
5688         beforeload : true,
5689         /**
5690          * @event load
5691          * Fires before the load method's callback is called.
5692          * @param {Object} This DataProxy object.
5693          * @param {Object} o The data object.
5694          * @param {Object} arg The callback argument object passed to the load function.
5695          */
5696         load : true,
5697         /**
5698          * @event loadexception
5699          * Fires if an Exception occurs during data retrieval.
5700          * @param {Object} This DataProxy object.
5701          * @param {Object} o The data object.
5702          * @param {Object} arg The callback argument object passed to the load function.
5703          * @param {Object} e The Exception.
5704          */
5705         loadexception : true
5706     });
5707     Roo.data.DataProxy.superclass.constructor.call(this);
5708 };
5709
5710 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5711
5712     /**
5713      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5714      */
5715 /*
5716  * Based on:
5717  * Ext JS Library 1.1.1
5718  * Copyright(c) 2006-2007, Ext JS, LLC.
5719  *
5720  * Originally Released Under LGPL - original licence link has changed is not relivant.
5721  *
5722  * Fork - LGPL
5723  * <script type="text/javascript">
5724  */
5725 /**
5726  * @class Roo.data.MemoryProxy
5727  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5728  * to the Reader when its load method is called.
5729  * @constructor
5730  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5731  */
5732 Roo.data.MemoryProxy = function(data){
5733     if (data.data) {
5734         data = data.data;
5735     }
5736     Roo.data.MemoryProxy.superclass.constructor.call(this);
5737     this.data = data;
5738 };
5739
5740 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5741     /**
5742      * Load data from the requested source (in this case an in-memory
5743      * data object passed to the constructor), read the data object into
5744      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5745      * process that block using the passed callback.
5746      * @param {Object} params This parameter is not used by the MemoryProxy class.
5747      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5748      * object into a block of Roo.data.Records.
5749      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5750      * The function must be passed <ul>
5751      * <li>The Record block object</li>
5752      * <li>The "arg" argument from the load function</li>
5753      * <li>A boolean success indicator</li>
5754      * </ul>
5755      * @param {Object} scope The scope in which to call the callback
5756      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5757      */
5758     load : function(params, reader, callback, scope, arg){
5759         params = params || {};
5760         var result;
5761         try {
5762             result = reader.readRecords(this.data);
5763         }catch(e){
5764             this.fireEvent("loadexception", this, arg, null, e);
5765             callback.call(scope, null, arg, false);
5766             return;
5767         }
5768         callback.call(scope, result, arg, true);
5769     },
5770     
5771     // private
5772     update : function(params, records){
5773         
5774     }
5775 });/*
5776  * Based on:
5777  * Ext JS Library 1.1.1
5778  * Copyright(c) 2006-2007, Ext JS, LLC.
5779  *
5780  * Originally Released Under LGPL - original licence link has changed is not relivant.
5781  *
5782  * Fork - LGPL
5783  * <script type="text/javascript">
5784  */
5785 /**
5786  * @class Roo.data.HttpProxy
5787  * @extends Roo.data.DataProxy
5788  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5789  * configured to reference a certain URL.<br><br>
5790  * <p>
5791  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5792  * from which the running page was served.<br><br>
5793  * <p>
5794  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5795  * <p>
5796  * Be aware that to enable the browser to parse an XML document, the server must set
5797  * the Content-Type header in the HTTP response to "text/xml".
5798  * @constructor
5799  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5800  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5801  * will be used to make the request.
5802  */
5803 Roo.data.HttpProxy = function(conn){
5804     Roo.data.HttpProxy.superclass.constructor.call(this);
5805     // is conn a conn config or a real conn?
5806     this.conn = conn;
5807     this.useAjax = !conn || !conn.events;
5808   
5809 };
5810
5811 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5812     // thse are take from connection...
5813     
5814     /**
5815      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5816      */
5817     /**
5818      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5819      * extra parameters to each request made by this object. (defaults to undefined)
5820      */
5821     /**
5822      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5823      *  to each request made by this object. (defaults to undefined)
5824      */
5825     /**
5826      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
5827      */
5828     /**
5829      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5830      */
5831      /**
5832      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5833      * @type Boolean
5834      */
5835   
5836
5837     /**
5838      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5839      * @type Boolean
5840      */
5841     /**
5842      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5843      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5844      * a finer-grained basis than the DataProxy events.
5845      */
5846     getConnection : function(){
5847         return this.useAjax ? Roo.Ajax : this.conn;
5848     },
5849
5850     /**
5851      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5852      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5853      * process that block using the passed callback.
5854      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5855      * for the request to the remote server.
5856      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5857      * object into a block of Roo.data.Records.
5858      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5859      * The function must be passed <ul>
5860      * <li>The Record block object</li>
5861      * <li>The "arg" argument from the load function</li>
5862      * <li>A boolean success indicator</li>
5863      * </ul>
5864      * @param {Object} scope The scope in which to call the callback
5865      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5866      */
5867     load : function(params, reader, callback, scope, arg){
5868         if(this.fireEvent("beforeload", this, params) !== false){
5869             var  o = {
5870                 params : params || {},
5871                 request: {
5872                     callback : callback,
5873                     scope : scope,
5874                     arg : arg
5875                 },
5876                 reader: reader,
5877                 callback : this.loadResponse,
5878                 scope: this
5879             };
5880             if(this.useAjax){
5881                 Roo.applyIf(o, this.conn);
5882                 if(this.activeRequest){
5883                     Roo.Ajax.abort(this.activeRequest);
5884                 }
5885                 this.activeRequest = Roo.Ajax.request(o);
5886             }else{
5887                 this.conn.request(o);
5888             }
5889         }else{
5890             callback.call(scope||this, null, arg, false);
5891         }
5892     },
5893
5894     // private
5895     loadResponse : function(o, success, response){
5896         delete this.activeRequest;
5897         if(!success){
5898             this.fireEvent("loadexception", this, o, response);
5899             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5900             return;
5901         }
5902         var result;
5903         try {
5904             result = o.reader.read(response);
5905         }catch(e){
5906             this.fireEvent("loadexception", this, o, response, e);
5907             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5908             return;
5909         }
5910         
5911         this.fireEvent("load", this, o, o.request.arg);
5912         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5913     },
5914
5915     // private
5916     update : function(dataSet){
5917
5918     },
5919
5920     // private
5921     updateResponse : function(dataSet){
5922
5923     }
5924 });/*
5925  * Based on:
5926  * Ext JS Library 1.1.1
5927  * Copyright(c) 2006-2007, Ext JS, LLC.
5928  *
5929  * Originally Released Under LGPL - original licence link has changed is not relivant.
5930  *
5931  * Fork - LGPL
5932  * <script type="text/javascript">
5933  */
5934
5935 /**
5936  * @class Roo.data.ScriptTagProxy
5937  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5938  * other than the originating domain of the running page.<br><br>
5939  * <p>
5940  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
5941  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5942  * <p>
5943  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5944  * source code that is used as the source inside a &lt;script> tag.<br><br>
5945  * <p>
5946  * In order for the browser to process the returned data, the server must wrap the data object
5947  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5948  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5949  * depending on whether the callback name was passed:
5950  * <p>
5951  * <pre><code>
5952 boolean scriptTag = false;
5953 String cb = request.getParameter("callback");
5954 if (cb != null) {
5955     scriptTag = true;
5956     response.setContentType("text/javascript");
5957 } else {
5958     response.setContentType("application/x-json");
5959 }
5960 Writer out = response.getWriter();
5961 if (scriptTag) {
5962     out.write(cb + "(");
5963 }
5964 out.print(dataBlock.toJsonString());
5965 if (scriptTag) {
5966     out.write(");");
5967 }
5968 </pre></code>
5969  *
5970  * @constructor
5971  * @param {Object} config A configuration object.
5972  */
5973 Roo.data.ScriptTagProxy = function(config){
5974     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5975     Roo.apply(this, config);
5976     this.head = document.getElementsByTagName("head")[0];
5977 };
5978
5979 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5980
5981 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5982     /**
5983      * @cfg {String} url The URL from which to request the data object.
5984      */
5985     /**
5986      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5987      */
5988     timeout : 30000,
5989     /**
5990      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5991      * the server the name of the callback function set up by the load call to process the returned data object.
5992      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5993      * javascript output which calls this named function passing the data object as its only parameter.
5994      */
5995     callbackParam : "callback",
5996     /**
5997      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5998      * name to the request.
5999      */
6000     nocache : true,
6001
6002     /**
6003      * Load data from the configured URL, read the data object into
6004      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
6005      * process that block using the passed callback.
6006      * @param {Object} params An object containing properties which are to be used as HTTP parameters
6007      * for the request to the remote server.
6008      * @param {Roo.data.DataReader} reader The Reader object which converts the data
6009      * object into a block of Roo.data.Records.
6010      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
6011      * The function must be passed <ul>
6012      * <li>The Record block object</li>
6013      * <li>The "arg" argument from the load function</li>
6014      * <li>A boolean success indicator</li>
6015      * </ul>
6016      * @param {Object} scope The scope in which to call the callback
6017      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
6018      */
6019     load : function(params, reader, callback, scope, arg){
6020         if(this.fireEvent("beforeload", this, params) !== false){
6021
6022             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
6023
6024             var url = this.url;
6025             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
6026             if(this.nocache){
6027                 url += "&_dc=" + (new Date().getTime());
6028             }
6029             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
6030             var trans = {
6031                 id : transId,
6032                 cb : "stcCallback"+transId,
6033                 scriptId : "stcScript"+transId,
6034                 params : params,
6035                 arg : arg,
6036                 url : url,
6037                 callback : callback,
6038                 scope : scope,
6039                 reader : reader
6040             };
6041             var conn = this;
6042
6043             window[trans.cb] = function(o){
6044                 conn.handleResponse(o, trans);
6045             };
6046
6047             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
6048
6049             if(this.autoAbort !== false){
6050                 this.abort();
6051             }
6052
6053             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6054
6055             var script = document.createElement("script");
6056             script.setAttribute("src", url);
6057             script.setAttribute("type", "text/javascript");
6058             script.setAttribute("id", trans.scriptId);
6059             this.head.appendChild(script);
6060
6061             this.trans = trans;
6062         }else{
6063             callback.call(scope||this, null, arg, false);
6064         }
6065     },
6066
6067     // private
6068     isLoading : function(){
6069         return this.trans ? true : false;
6070     },
6071
6072     /**
6073      * Abort the current server request.
6074      */
6075     abort : function(){
6076         if(this.isLoading()){
6077             this.destroyTrans(this.trans);
6078         }
6079     },
6080
6081     // private
6082     destroyTrans : function(trans, isLoaded){
6083         this.head.removeChild(document.getElementById(trans.scriptId));
6084         clearTimeout(trans.timeoutId);
6085         if(isLoaded){
6086             window[trans.cb] = undefined;
6087             try{
6088                 delete window[trans.cb];
6089             }catch(e){}
6090         }else{
6091             // if hasn't been loaded, wait for load to remove it to prevent script error
6092             window[trans.cb] = function(){
6093                 window[trans.cb] = undefined;
6094                 try{
6095                     delete window[trans.cb];
6096                 }catch(e){}
6097             };
6098         }
6099     },
6100
6101     // private
6102     handleResponse : function(o, trans){
6103         this.trans = false;
6104         this.destroyTrans(trans, true);
6105         var result;
6106         try {
6107             result = trans.reader.readRecords(o);
6108         }catch(e){
6109             this.fireEvent("loadexception", this, o, trans.arg, e);
6110             trans.callback.call(trans.scope||window, null, trans.arg, false);
6111             return;
6112         }
6113         this.fireEvent("load", this, o, trans.arg);
6114         trans.callback.call(trans.scope||window, result, trans.arg, true);
6115     },
6116
6117     // private
6118     handleFailure : function(trans){
6119         this.trans = false;
6120         this.destroyTrans(trans, false);
6121         this.fireEvent("loadexception", this, null, trans.arg);
6122         trans.callback.call(trans.scope||window, null, trans.arg, false);
6123     }
6124 });/*
6125  * Based on:
6126  * Ext JS Library 1.1.1
6127  * Copyright(c) 2006-2007, Ext JS, LLC.
6128  *
6129  * Originally Released Under LGPL - original licence link has changed is not relivant.
6130  *
6131  * Fork - LGPL
6132  * <script type="text/javascript">
6133  */
6134
6135 /**
6136  * @class Roo.data.JsonReader
6137  * @extends Roo.data.DataReader
6138  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6139  * based on mappings in a provided Roo.data.Record constructor.
6140  * 
6141  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6142  * in the reply previously. 
6143  * 
6144  * <p>
6145  * Example code:
6146  * <pre><code>
6147 var RecordDef = Roo.data.Record.create([
6148     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6149     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6150 ]);
6151 var myReader = new Roo.data.JsonReader({
6152     totalProperty: "results",    // The property which contains the total dataset size (optional)
6153     root: "rows",                // The property which contains an Array of row objects
6154     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6155 }, RecordDef);
6156 </code></pre>
6157  * <p>
6158  * This would consume a JSON file like this:
6159  * <pre><code>
6160 { 'results': 2, 'rows': [
6161     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6162     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6163 }
6164 </code></pre>
6165  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6166  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6167  * paged from the remote server.
6168  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6169  * @cfg {String} root name of the property which contains the Array of row objects.
6170  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6171  * @cfg {Array} fields Array of field definition objects
6172  * @constructor
6173  * Create a new JsonReader
6174  * @param {Object} meta Metadata configuration options
6175  * @param {Object} recordType Either an Array of field definition objects,
6176  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6177  */
6178 Roo.data.JsonReader = function(meta, recordType){
6179     
6180     meta = meta || {};
6181     // set some defaults:
6182     Roo.applyIf(meta, {
6183         totalProperty: 'total',
6184         successProperty : 'success',
6185         root : 'data',
6186         id : 'id'
6187     });
6188     
6189     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6190 };
6191 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6192     
6193     /**
6194      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6195      * Used by Store query builder to append _requestMeta to params.
6196      * 
6197      */
6198     metaFromRemote : false,
6199     /**
6200      * This method is only used by a DataProxy which has retrieved data from a remote server.
6201      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6202      * @return {Object} data A data block which is used by an Roo.data.Store object as
6203      * a cache of Roo.data.Records.
6204      */
6205     read : function(response){
6206         var json = response.responseText;
6207        
6208         var o = /* eval:var:o */ eval("("+json+")");
6209         if(!o) {
6210             throw {message: "JsonReader.read: Json object not found"};
6211         }
6212         
6213         if(o.metaData){
6214             
6215             delete this.ef;
6216             this.metaFromRemote = true;
6217             this.meta = o.metaData;
6218             this.recordType = Roo.data.Record.create(o.metaData.fields);
6219             this.onMetaChange(this.meta, this.recordType, o);
6220         }
6221         return this.readRecords(o);
6222     },
6223
6224     // private function a store will implement
6225     onMetaChange : function(meta, recordType, o){
6226
6227     },
6228
6229     /**
6230          * @ignore
6231          */
6232     simpleAccess: function(obj, subsc) {
6233         return obj[subsc];
6234     },
6235
6236         /**
6237          * @ignore
6238          */
6239     getJsonAccessor: function(){
6240         var re = /[\[\.]/;
6241         return function(expr) {
6242             try {
6243                 return(re.test(expr))
6244                     ? new Function("obj", "return obj." + expr)
6245                     : function(obj){
6246                         return obj[expr];
6247                     };
6248             } catch(e){}
6249             return Roo.emptyFn;
6250         };
6251     }(),
6252
6253     /**
6254      * Create a data block containing Roo.data.Records from an XML document.
6255      * @param {Object} o An object which contains an Array of row objects in the property specified
6256      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6257      * which contains the total size of the dataset.
6258      * @return {Object} data A data block which is used by an Roo.data.Store object as
6259      * a cache of Roo.data.Records.
6260      */
6261     readRecords : function(o){
6262         /**
6263          * After any data loads, the raw JSON data is available for further custom processing.
6264          * @type Object
6265          */
6266         this.o = o;
6267         var s = this.meta, Record = this.recordType,
6268             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
6269
6270 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6271         if (!this.ef) {
6272             if(s.totalProperty) {
6273                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6274                 }
6275                 if(s.successProperty) {
6276                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6277                 }
6278                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6279                 if (s.id) {
6280                         var g = this.getJsonAccessor(s.id);
6281                         this.getId = function(rec) {
6282                                 var r = g(rec);  
6283                                 return (r === undefined || r === "") ? null : r;
6284                         };
6285                 } else {
6286                         this.getId = function(){return null;};
6287                 }
6288             this.ef = [];
6289             for(var jj = 0; jj < fl; jj++){
6290                 f = fi[jj];
6291                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6292                 this.ef[jj] = this.getJsonAccessor(map);
6293             }
6294         }
6295
6296         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6297         if(s.totalProperty){
6298             var vt = parseInt(this.getTotal(o), 10);
6299             if(!isNaN(vt)){
6300                 totalRecords = vt;
6301             }
6302         }
6303         if(s.successProperty){
6304             var vs = this.getSuccess(o);
6305             if(vs === false || vs === 'false'){
6306                 success = false;
6307             }
6308         }
6309         var records = [];
6310         for(var i = 0; i < c; i++){
6311                 var n = root[i];
6312             var values = {};
6313             var id = this.getId(n);
6314             for(var j = 0; j < fl; j++){
6315                 f = fi[j];
6316             var v = this.ef[j](n);
6317             if (!f.convert) {
6318                 Roo.log('missing convert for ' + f.name);
6319                 Roo.log(f);
6320                 continue;
6321             }
6322             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6323             }
6324             var record = new Record(values, id);
6325             record.json = n;
6326             records[i] = record;
6327         }
6328         return {
6329             raw : o,
6330             success : success,
6331             records : records,
6332             totalRecords : totalRecords
6333         };
6334     }
6335 });/*
6336  * Based on:
6337  * Ext JS Library 1.1.1
6338  * Copyright(c) 2006-2007, Ext JS, LLC.
6339  *
6340  * Originally Released Under LGPL - original licence link has changed is not relivant.
6341  *
6342  * Fork - LGPL
6343  * <script type="text/javascript">
6344  */
6345
6346 /**
6347  * @class Roo.data.XmlReader
6348  * @extends Roo.data.DataReader
6349  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6350  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6351  * <p>
6352  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6353  * header in the HTTP response must be set to "text/xml".</em>
6354  * <p>
6355  * Example code:
6356  * <pre><code>
6357 var RecordDef = Roo.data.Record.create([
6358    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6359    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6360 ]);
6361 var myReader = new Roo.data.XmlReader({
6362    totalRecords: "results", // The element which contains the total dataset size (optional)
6363    record: "row",           // The repeated element which contains row information
6364    id: "id"                 // The element within the row that provides an ID for the record (optional)
6365 }, RecordDef);
6366 </code></pre>
6367  * <p>
6368  * This would consume an XML file like this:
6369  * <pre><code>
6370 &lt;?xml?>
6371 &lt;dataset>
6372  &lt;results>2&lt;/results>
6373  &lt;row>
6374    &lt;id>1&lt;/id>
6375    &lt;name>Bill&lt;/name>
6376    &lt;occupation>Gardener&lt;/occupation>
6377  &lt;/row>
6378  &lt;row>
6379    &lt;id>2&lt;/id>
6380    &lt;name>Ben&lt;/name>
6381    &lt;occupation>Horticulturalist&lt;/occupation>
6382  &lt;/row>
6383 &lt;/dataset>
6384 </code></pre>
6385  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6386  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6387  * paged from the remote server.
6388  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6389  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6390  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6391  * a record identifier value.
6392  * @constructor
6393  * Create a new XmlReader
6394  * @param {Object} meta Metadata configuration options
6395  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6396  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6397  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6398  */
6399 Roo.data.XmlReader = function(meta, recordType){
6400     meta = meta || {};
6401     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6402 };
6403 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6404     /**
6405      * This method is only used by a DataProxy which has retrieved data from a remote server.
6406          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6407          * to contain a method called 'responseXML' that returns an XML document object.
6408      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6409      * a cache of Roo.data.Records.
6410      */
6411     read : function(response){
6412         var doc = response.responseXML;
6413         if(!doc) {
6414             throw {message: "XmlReader.read: XML Document not available"};
6415         }
6416         return this.readRecords(doc);
6417     },
6418
6419     /**
6420      * Create a data block containing Roo.data.Records from an XML document.
6421          * @param {Object} doc A parsed XML document.
6422      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6423      * a cache of Roo.data.Records.
6424      */
6425     readRecords : function(doc){
6426         /**
6427          * After any data loads/reads, the raw XML Document is available for further custom processing.
6428          * @type XMLDocument
6429          */
6430         this.xmlData = doc;
6431         var root = doc.documentElement || doc;
6432         var q = Roo.DomQuery;
6433         var recordType = this.recordType, fields = recordType.prototype.fields;
6434         var sid = this.meta.id;
6435         var totalRecords = 0, success = true;
6436         if(this.meta.totalRecords){
6437             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6438         }
6439         
6440         if(this.meta.success){
6441             var sv = q.selectValue(this.meta.success, root, true);
6442             success = sv !== false && sv !== 'false';
6443         }
6444         var records = [];
6445         var ns = q.select(this.meta.record, root);
6446         for(var i = 0, len = ns.length; i < len; i++) {
6447                 var n = ns[i];
6448                 var values = {};
6449                 var id = sid ? q.selectValue(sid, n) : undefined;
6450                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6451                     var f = fields.items[j];
6452                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6453                     v = f.convert(v);
6454                     values[f.name] = v;
6455                 }
6456                 var record = new recordType(values, id);
6457                 record.node = n;
6458                 records[records.length] = record;
6459             }
6460
6461             return {
6462                 success : success,
6463                 records : records,
6464                 totalRecords : totalRecords || records.length
6465             };
6466     }
6467 });/*
6468  * Based on:
6469  * Ext JS Library 1.1.1
6470  * Copyright(c) 2006-2007, Ext JS, LLC.
6471  *
6472  * Originally Released Under LGPL - original licence link has changed is not relivant.
6473  *
6474  * Fork - LGPL
6475  * <script type="text/javascript">
6476  */
6477
6478 /**
6479  * @class Roo.data.ArrayReader
6480  * @extends Roo.data.DataReader
6481  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6482  * Each element of that Array represents a row of data fields. The
6483  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6484  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6485  * <p>
6486  * Example code:.
6487  * <pre><code>
6488 var RecordDef = Roo.data.Record.create([
6489     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6490     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6491 ]);
6492 var myReader = new Roo.data.ArrayReader({
6493     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6494 }, RecordDef);
6495 </code></pre>
6496  * <p>
6497  * This would consume an Array like this:
6498  * <pre><code>
6499 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6500   </code></pre>
6501  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6502  * @constructor
6503  * Create a new JsonReader
6504  * @param {Object} meta Metadata configuration options.
6505  * @param {Object} recordType Either an Array of field definition objects
6506  * as specified to {@link Roo.data.Record#create},
6507  * or an {@link Roo.data.Record} object
6508  * created using {@link Roo.data.Record#create}.
6509  */
6510 Roo.data.ArrayReader = function(meta, recordType){
6511     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6512 };
6513
6514 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6515     /**
6516      * Create a data block containing Roo.data.Records from an XML document.
6517      * @param {Object} o An Array of row objects which represents the dataset.
6518      * @return {Object} data A data block which is used by an Roo.data.Store object as
6519      * a cache of Roo.data.Records.
6520      */
6521     readRecords : function(o){
6522         var sid = this.meta ? this.meta.id : null;
6523         var recordType = this.recordType, fields = recordType.prototype.fields;
6524         var records = [];
6525         var root = o;
6526             for(var i = 0; i < root.length; i++){
6527                     var n = root[i];
6528                 var values = {};
6529                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6530                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6531                 var f = fields.items[j];
6532                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6533                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6534                 v = f.convert(v);
6535                 values[f.name] = v;
6536             }
6537                 var record = new recordType(values, id);
6538                 record.json = n;
6539                 records[records.length] = record;
6540             }
6541             return {
6542                 records : records,
6543                 totalRecords : records.length
6544             };
6545     }
6546 });/*
6547  * Based on:
6548  * Ext JS Library 1.1.1
6549  * Copyright(c) 2006-2007, Ext JS, LLC.
6550  *
6551  * Originally Released Under LGPL - original licence link has changed is not relivant.
6552  *
6553  * Fork - LGPL
6554  * <script type="text/javascript">
6555  */
6556
6557
6558 /**
6559  * @class Roo.data.Tree
6560  * @extends Roo.util.Observable
6561  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6562  * in the tree have most standard DOM functionality.
6563  * @constructor
6564  * @param {Node} root (optional) The root node
6565  */
6566 Roo.data.Tree = function(root){
6567    this.nodeHash = {};
6568    /**
6569     * The root node for this tree
6570     * @type Node
6571     */
6572    this.root = null;
6573    if(root){
6574        this.setRootNode(root);
6575    }
6576    this.addEvents({
6577        /**
6578         * @event append
6579         * Fires when a new child node is appended to a node in this tree.
6580         * @param {Tree} tree The owner tree
6581         * @param {Node} parent The parent node
6582         * @param {Node} node The newly appended node
6583         * @param {Number} index The index of the newly appended node
6584         */
6585        "append" : true,
6586        /**
6587         * @event remove
6588         * Fires when a child node is removed from a node in this tree.
6589         * @param {Tree} tree The owner tree
6590         * @param {Node} parent The parent node
6591         * @param {Node} node The child node removed
6592         */
6593        "remove" : true,
6594        /**
6595         * @event move
6596         * Fires when a node is moved to a new location in the tree
6597         * @param {Tree} tree The owner tree
6598         * @param {Node} node The node moved
6599         * @param {Node} oldParent The old parent of this node
6600         * @param {Node} newParent The new parent of this node
6601         * @param {Number} index The index it was moved to
6602         */
6603        "move" : true,
6604        /**
6605         * @event insert
6606         * Fires when a new child node is inserted in a node in this tree.
6607         * @param {Tree} tree The owner tree
6608         * @param {Node} parent The parent node
6609         * @param {Node} node The child node inserted
6610         * @param {Node} refNode The child node the node was inserted before
6611         */
6612        "insert" : true,
6613        /**
6614         * @event beforeappend
6615         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6616         * @param {Tree} tree The owner tree
6617         * @param {Node} parent The parent node
6618         * @param {Node} node The child node to be appended
6619         */
6620        "beforeappend" : true,
6621        /**
6622         * @event beforeremove
6623         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6624         * @param {Tree} tree The owner tree
6625         * @param {Node} parent The parent node
6626         * @param {Node} node The child node to be removed
6627         */
6628        "beforeremove" : true,
6629        /**
6630         * @event beforemove
6631         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6632         * @param {Tree} tree The owner tree
6633         * @param {Node} node The node being moved
6634         * @param {Node} oldParent The parent of the node
6635         * @param {Node} newParent The new parent the node is moving to
6636         * @param {Number} index The index it is being moved to
6637         */
6638        "beforemove" : true,
6639        /**
6640         * @event beforeinsert
6641         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6642         * @param {Tree} tree The owner tree
6643         * @param {Node} parent The parent node
6644         * @param {Node} node The child node to be inserted
6645         * @param {Node} refNode The child node the node is being inserted before
6646         */
6647        "beforeinsert" : true
6648    });
6649
6650     Roo.data.Tree.superclass.constructor.call(this);
6651 };
6652
6653 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6654     pathSeparator: "/",
6655
6656     proxyNodeEvent : function(){
6657         return this.fireEvent.apply(this, arguments);
6658     },
6659
6660     /**
6661      * Returns the root node for this tree.
6662      * @return {Node}
6663      */
6664     getRootNode : function(){
6665         return this.root;
6666     },
6667
6668     /**
6669      * Sets the root node for this tree.
6670      * @param {Node} node
6671      * @return {Node}
6672      */
6673     setRootNode : function(node){
6674         this.root = node;
6675         node.ownerTree = this;
6676         node.isRoot = true;
6677         this.registerNode(node);
6678         return node;
6679     },
6680
6681     /**
6682      * Gets a node in this tree by its id.
6683      * @param {String} id
6684      * @return {Node}
6685      */
6686     getNodeById : function(id){
6687         return this.nodeHash[id];
6688     },
6689
6690     registerNode : function(node){
6691         this.nodeHash[node.id] = node;
6692     },
6693
6694     unregisterNode : function(node){
6695         delete this.nodeHash[node.id];
6696     },
6697
6698     toString : function(){
6699         return "[Tree"+(this.id?" "+this.id:"")+"]";
6700     }
6701 });
6702
6703 /**
6704  * @class Roo.data.Node
6705  * @extends Roo.util.Observable
6706  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6707  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6708  * @constructor
6709  * @param {Object} attributes The attributes/config for the node
6710  */
6711 Roo.data.Node = function(attributes){
6712     /**
6713      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6714      * @type {Object}
6715      */
6716     this.attributes = attributes || {};
6717     this.leaf = this.attributes.leaf;
6718     /**
6719      * The node id. @type String
6720      */
6721     this.id = this.attributes.id;
6722     if(!this.id){
6723         this.id = Roo.id(null, "ynode-");
6724         this.attributes.id = this.id;
6725     }
6726      
6727     
6728     /**
6729      * All child nodes of this node. @type Array
6730      */
6731     this.childNodes = [];
6732     if(!this.childNodes.indexOf){ // indexOf is a must
6733         this.childNodes.indexOf = function(o){
6734             for(var i = 0, len = this.length; i < len; i++){
6735                 if(this[i] == o) {
6736                     return i;
6737                 }
6738             }
6739             return -1;
6740         };
6741     }
6742     /**
6743      * The parent node for this node. @type Node
6744      */
6745     this.parentNode = null;
6746     /**
6747      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6748      */
6749     this.firstChild = null;
6750     /**
6751      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6752      */
6753     this.lastChild = null;
6754     /**
6755      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6756      */
6757     this.previousSibling = null;
6758     /**
6759      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6760      */
6761     this.nextSibling = null;
6762
6763     this.addEvents({
6764        /**
6765         * @event append
6766         * Fires when a new child node is appended
6767         * @param {Tree} tree The owner tree
6768         * @param {Node} this This node
6769         * @param {Node} node The newly appended node
6770         * @param {Number} index The index of the newly appended node
6771         */
6772        "append" : true,
6773        /**
6774         * @event remove
6775         * Fires when a child node is removed
6776         * @param {Tree} tree The owner tree
6777         * @param {Node} this This node
6778         * @param {Node} node The removed node
6779         */
6780        "remove" : true,
6781        /**
6782         * @event move
6783         * Fires when this node is moved to a new location in the tree
6784         * @param {Tree} tree The owner tree
6785         * @param {Node} this This node
6786         * @param {Node} oldParent The old parent of this node
6787         * @param {Node} newParent The new parent of this node
6788         * @param {Number} index The index it was moved to
6789         */
6790        "move" : true,
6791        /**
6792         * @event insert
6793         * Fires when a new child node is inserted.
6794         * @param {Tree} tree The owner tree
6795         * @param {Node} this This node
6796         * @param {Node} node The child node inserted
6797         * @param {Node} refNode The child node the node was inserted before
6798         */
6799        "insert" : true,
6800        /**
6801         * @event beforeappend
6802         * Fires before a new child is appended, return false to cancel the append.
6803         * @param {Tree} tree The owner tree
6804         * @param {Node} this This node
6805         * @param {Node} node The child node to be appended
6806         */
6807        "beforeappend" : true,
6808        /**
6809         * @event beforeremove
6810         * Fires before a child is removed, return false to cancel the remove.
6811         * @param {Tree} tree The owner tree
6812         * @param {Node} this This node
6813         * @param {Node} node The child node to be removed
6814         */
6815        "beforeremove" : true,
6816        /**
6817         * @event beforemove
6818         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6819         * @param {Tree} tree The owner tree
6820         * @param {Node} this This node
6821         * @param {Node} oldParent The parent of this node
6822         * @param {Node} newParent The new parent this node is moving to
6823         * @param {Number} index The index it is being moved to
6824         */
6825        "beforemove" : true,
6826        /**
6827         * @event beforeinsert
6828         * Fires before a new child is inserted, return false to cancel the insert.
6829         * @param {Tree} tree The owner tree
6830         * @param {Node} this This node
6831         * @param {Node} node The child node to be inserted
6832         * @param {Node} refNode The child node the node is being inserted before
6833         */
6834        "beforeinsert" : true
6835    });
6836     this.listeners = this.attributes.listeners;
6837     Roo.data.Node.superclass.constructor.call(this);
6838 };
6839
6840 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6841     fireEvent : function(evtName){
6842         // first do standard event for this node
6843         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6844             return false;
6845         }
6846         // then bubble it up to the tree if the event wasn't cancelled
6847         var ot = this.getOwnerTree();
6848         if(ot){
6849             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6850                 return false;
6851             }
6852         }
6853         return true;
6854     },
6855
6856     /**
6857      * Returns true if this node is a leaf
6858      * @return {Boolean}
6859      */
6860     isLeaf : function(){
6861         return this.leaf === true;
6862     },
6863
6864     // private
6865     setFirstChild : function(node){
6866         this.firstChild = node;
6867     },
6868
6869     //private
6870     setLastChild : function(node){
6871         this.lastChild = node;
6872     },
6873
6874
6875     /**
6876      * Returns true if this node is the last child of its parent
6877      * @return {Boolean}
6878      */
6879     isLast : function(){
6880        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6881     },
6882
6883     /**
6884      * Returns true if this node is the first child of its parent
6885      * @return {Boolean}
6886      */
6887     isFirst : function(){
6888        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6889     },
6890
6891     hasChildNodes : function(){
6892         return !this.isLeaf() && this.childNodes.length > 0;
6893     },
6894
6895     /**
6896      * Insert node(s) as the last child node of this node.
6897      * @param {Node/Array} node The node or Array of nodes to append
6898      * @return {Node} The appended node if single append, or null if an array was passed
6899      */
6900     appendChild : function(node){
6901         var multi = false;
6902         if(node instanceof Array){
6903             multi = node;
6904         }else if(arguments.length > 1){
6905             multi = arguments;
6906         }
6907         // if passed an array or multiple args do them one by one
6908         if(multi){
6909             for(var i = 0, len = multi.length; i < len; i++) {
6910                 this.appendChild(multi[i]);
6911             }
6912         }else{
6913             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6914                 return false;
6915             }
6916             var index = this.childNodes.length;
6917             var oldParent = node.parentNode;
6918             // it's a move, make sure we move it cleanly
6919             if(oldParent){
6920                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6921                     return false;
6922                 }
6923                 oldParent.removeChild(node);
6924             }
6925             index = this.childNodes.length;
6926             if(index == 0){
6927                 this.setFirstChild(node);
6928             }
6929             this.childNodes.push(node);
6930             node.parentNode = this;
6931             var ps = this.childNodes[index-1];
6932             if(ps){
6933                 node.previousSibling = ps;
6934                 ps.nextSibling = node;
6935             }else{
6936                 node.previousSibling = null;
6937             }
6938             node.nextSibling = null;
6939             this.setLastChild(node);
6940             node.setOwnerTree(this.getOwnerTree());
6941             this.fireEvent("append", this.ownerTree, this, node, index);
6942             if(oldParent){
6943                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6944             }
6945             return node;
6946         }
6947     },
6948
6949     /**
6950      * Removes a child node from this node.
6951      * @param {Node} node The node to remove
6952      * @return {Node} The removed node
6953      */
6954     removeChild : function(node){
6955         var index = this.childNodes.indexOf(node);
6956         if(index == -1){
6957             return false;
6958         }
6959         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6960             return false;
6961         }
6962
6963         // remove it from childNodes collection
6964         this.childNodes.splice(index, 1);
6965
6966         // update siblings
6967         if(node.previousSibling){
6968             node.previousSibling.nextSibling = node.nextSibling;
6969         }
6970         if(node.nextSibling){
6971             node.nextSibling.previousSibling = node.previousSibling;
6972         }
6973
6974         // update child refs
6975         if(this.firstChild == node){
6976             this.setFirstChild(node.nextSibling);
6977         }
6978         if(this.lastChild == node){
6979             this.setLastChild(node.previousSibling);
6980         }
6981
6982         node.setOwnerTree(null);
6983         // clear any references from the node
6984         node.parentNode = null;
6985         node.previousSibling = null;
6986         node.nextSibling = null;
6987         this.fireEvent("remove", this.ownerTree, this, node);
6988         return node;
6989     },
6990
6991     /**
6992      * Inserts the first node before the second node in this nodes childNodes collection.
6993      * @param {Node} node The node to insert
6994      * @param {Node} refNode The node to insert before (if null the node is appended)
6995      * @return {Node} The inserted node
6996      */
6997     insertBefore : function(node, refNode){
6998         if(!refNode){ // like standard Dom, refNode can be null for append
6999             return this.appendChild(node);
7000         }
7001         // nothing to do
7002         if(node == refNode){
7003             return false;
7004         }
7005
7006         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
7007             return false;
7008         }
7009         var index = this.childNodes.indexOf(refNode);
7010         var oldParent = node.parentNode;
7011         var refIndex = index;
7012
7013         // when moving internally, indexes will change after remove
7014         if(oldParent == this && this.childNodes.indexOf(node) < index){
7015             refIndex--;
7016         }
7017
7018         // it's a move, make sure we move it cleanly
7019         if(oldParent){
7020             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
7021                 return false;
7022             }
7023             oldParent.removeChild(node);
7024         }
7025         if(refIndex == 0){
7026             this.setFirstChild(node);
7027         }
7028         this.childNodes.splice(refIndex, 0, node);
7029         node.parentNode = this;
7030         var ps = this.childNodes[refIndex-1];
7031         if(ps){
7032             node.previousSibling = ps;
7033             ps.nextSibling = node;
7034         }else{
7035             node.previousSibling = null;
7036         }
7037         node.nextSibling = refNode;
7038         refNode.previousSibling = node;
7039         node.setOwnerTree(this.getOwnerTree());
7040         this.fireEvent("insert", this.ownerTree, this, node, refNode);
7041         if(oldParent){
7042             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
7043         }
7044         return node;
7045     },
7046
7047     /**
7048      * Returns the child node at the specified index.
7049      * @param {Number} index
7050      * @return {Node}
7051      */
7052     item : function(index){
7053         return this.childNodes[index];
7054     },
7055
7056     /**
7057      * Replaces one child node in this node with another.
7058      * @param {Node} newChild The replacement node
7059      * @param {Node} oldChild The node to replace
7060      * @return {Node} The replaced node
7061      */
7062     replaceChild : function(newChild, oldChild){
7063         this.insertBefore(newChild, oldChild);
7064         this.removeChild(oldChild);
7065         return oldChild;
7066     },
7067
7068     /**
7069      * Returns the index of a child node
7070      * @param {Node} node
7071      * @return {Number} The index of the node or -1 if it was not found
7072      */
7073     indexOf : function(child){
7074         return this.childNodes.indexOf(child);
7075     },
7076
7077     /**
7078      * Returns the tree this node is in.
7079      * @return {Tree}
7080      */
7081     getOwnerTree : function(){
7082         // if it doesn't have one, look for one
7083         if(!this.ownerTree){
7084             var p = this;
7085             while(p){
7086                 if(p.ownerTree){
7087                     this.ownerTree = p.ownerTree;
7088                     break;
7089                 }
7090                 p = p.parentNode;
7091             }
7092         }
7093         return this.ownerTree;
7094     },
7095
7096     /**
7097      * Returns depth of this node (the root node has a depth of 0)
7098      * @return {Number}
7099      */
7100     getDepth : function(){
7101         var depth = 0;
7102         var p = this;
7103         while(p.parentNode){
7104             ++depth;
7105             p = p.parentNode;
7106         }
7107         return depth;
7108     },
7109
7110     // private
7111     setOwnerTree : function(tree){
7112         // if it's move, we need to update everyone
7113         if(tree != this.ownerTree){
7114             if(this.ownerTree){
7115                 this.ownerTree.unregisterNode(this);
7116             }
7117             this.ownerTree = tree;
7118             var cs = this.childNodes;
7119             for(var i = 0, len = cs.length; i < len; i++) {
7120                 cs[i].setOwnerTree(tree);
7121             }
7122             if(tree){
7123                 tree.registerNode(this);
7124             }
7125         }
7126     },
7127
7128     /**
7129      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7130      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7131      * @return {String} The path
7132      */
7133     getPath : function(attr){
7134         attr = attr || "id";
7135         var p = this.parentNode;
7136         var b = [this.attributes[attr]];
7137         while(p){
7138             b.unshift(p.attributes[attr]);
7139             p = p.parentNode;
7140         }
7141         var sep = this.getOwnerTree().pathSeparator;
7142         return sep + b.join(sep);
7143     },
7144
7145     /**
7146      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7147      * function call will be the scope provided or the current node. The arguments to the function
7148      * will be the args provided or the current node. If the function returns false at any point,
7149      * the bubble is stopped.
7150      * @param {Function} fn The function to call
7151      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7152      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7153      */
7154     bubble : function(fn, scope, args){
7155         var p = this;
7156         while(p){
7157             if(fn.call(scope || p, args || p) === false){
7158                 break;
7159             }
7160             p = p.parentNode;
7161         }
7162     },
7163
7164     /**
7165      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7166      * function call will be the scope provided or the current node. The arguments to the function
7167      * will be the args provided or the current node. If the function returns false at any point,
7168      * the cascade is stopped on that branch.
7169      * @param {Function} fn The function to call
7170      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7171      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7172      */
7173     cascade : function(fn, scope, args){
7174         if(fn.call(scope || this, args || this) !== false){
7175             var cs = this.childNodes;
7176             for(var i = 0, len = cs.length; i < len; i++) {
7177                 cs[i].cascade(fn, scope, args);
7178             }
7179         }
7180     },
7181
7182     /**
7183      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7184      * function call will be the scope provided or the current node. The arguments to the function
7185      * will be the args provided or the current node. If the function returns false at any point,
7186      * the iteration stops.
7187      * @param {Function} fn The function to call
7188      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7189      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7190      */
7191     eachChild : function(fn, scope, args){
7192         var cs = this.childNodes;
7193         for(var i = 0, len = cs.length; i < len; i++) {
7194                 if(fn.call(scope || this, args || cs[i]) === false){
7195                     break;
7196                 }
7197         }
7198     },
7199
7200     /**
7201      * Finds the first child that has the attribute with the specified value.
7202      * @param {String} attribute The attribute name
7203      * @param {Mixed} value The value to search for
7204      * @return {Node} The found child or null if none was found
7205      */
7206     findChild : function(attribute, value){
7207         var cs = this.childNodes;
7208         for(var i = 0, len = cs.length; i < len; i++) {
7209                 if(cs[i].attributes[attribute] == value){
7210                     return cs[i];
7211                 }
7212         }
7213         return null;
7214     },
7215
7216     /**
7217      * Finds the first child by a custom function. The child matches if the function passed
7218      * returns true.
7219      * @param {Function} fn
7220      * @param {Object} scope (optional)
7221      * @return {Node} The found child or null if none was found
7222      */
7223     findChildBy : function(fn, scope){
7224         var cs = this.childNodes;
7225         for(var i = 0, len = cs.length; i < len; i++) {
7226                 if(fn.call(scope||cs[i], cs[i]) === true){
7227                     return cs[i];
7228                 }
7229         }
7230         return null;
7231     },
7232
7233     /**
7234      * Sorts this nodes children using the supplied sort function
7235      * @param {Function} fn
7236      * @param {Object} scope (optional)
7237      */
7238     sort : function(fn, scope){
7239         var cs = this.childNodes;
7240         var len = cs.length;
7241         if(len > 0){
7242             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7243             cs.sort(sortFn);
7244             for(var i = 0; i < len; i++){
7245                 var n = cs[i];
7246                 n.previousSibling = cs[i-1];
7247                 n.nextSibling = cs[i+1];
7248                 if(i == 0){
7249                     this.setFirstChild(n);
7250                 }
7251                 if(i == len-1){
7252                     this.setLastChild(n);
7253                 }
7254             }
7255         }
7256     },
7257
7258     /**
7259      * Returns true if this node is an ancestor (at any point) of the passed node.
7260      * @param {Node} node
7261      * @return {Boolean}
7262      */
7263     contains : function(node){
7264         return node.isAncestor(this);
7265     },
7266
7267     /**
7268      * Returns true if the passed node is an ancestor (at any point) of this node.
7269      * @param {Node} node
7270      * @return {Boolean}
7271      */
7272     isAncestor : function(node){
7273         var p = this.parentNode;
7274         while(p){
7275             if(p == node){
7276                 return true;
7277             }
7278             p = p.parentNode;
7279         }
7280         return false;
7281     },
7282
7283     toString : function(){
7284         return "[Node"+(this.id?" "+this.id:"")+"]";
7285     }
7286 });/*
7287  * Based on:
7288  * Ext JS Library 1.1.1
7289  * Copyright(c) 2006-2007, Ext JS, LLC.
7290  *
7291  * Originally Released Under LGPL - original licence link has changed is not relivant.
7292  *
7293  * Fork - LGPL
7294  * <script type="text/javascript">
7295  */
7296  (function(){ 
7297 /**
7298  * @class Roo.Layer
7299  * @extends Roo.Element
7300  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7301  * automatic maintaining of shadow/shim positions.
7302  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7303  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7304  * you can pass a string with a CSS class name. False turns off the shadow.
7305  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7306  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7307  * @cfg {String} cls CSS class to add to the element
7308  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7309  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7310  * @constructor
7311  * @param {Object} config An object with config options.
7312  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7313  */
7314
7315 Roo.Layer = function(config, existingEl){
7316     config = config || {};
7317     var dh = Roo.DomHelper;
7318     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7319     if(existingEl){
7320         this.dom = Roo.getDom(existingEl);
7321     }
7322     if(!this.dom){
7323         var o = config.dh || {tag: "div", cls: "x-layer"};
7324         this.dom = dh.append(pel, o);
7325     }
7326     if(config.cls){
7327         this.addClass(config.cls);
7328     }
7329     this.constrain = config.constrain !== false;
7330     this.visibilityMode = Roo.Element.VISIBILITY;
7331     if(config.id){
7332         this.id = this.dom.id = config.id;
7333     }else{
7334         this.id = Roo.id(this.dom);
7335     }
7336     this.zindex = config.zindex || this.getZIndex();
7337     this.position("absolute", this.zindex);
7338     if(config.shadow){
7339         this.shadowOffset = config.shadowOffset || 4;
7340         this.shadow = new Roo.Shadow({
7341             offset : this.shadowOffset,
7342             mode : config.shadow
7343         });
7344     }else{
7345         this.shadowOffset = 0;
7346     }
7347     this.useShim = config.shim !== false && Roo.useShims;
7348     this.useDisplay = config.useDisplay;
7349     this.hide();
7350 };
7351
7352 var supr = Roo.Element.prototype;
7353
7354 // shims are shared among layer to keep from having 100 iframes
7355 var shims = [];
7356
7357 Roo.extend(Roo.Layer, Roo.Element, {
7358
7359     getZIndex : function(){
7360         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7361     },
7362
7363     getShim : function(){
7364         if(!this.useShim){
7365             return null;
7366         }
7367         if(this.shim){
7368             return this.shim;
7369         }
7370         var shim = shims.shift();
7371         if(!shim){
7372             shim = this.createShim();
7373             shim.enableDisplayMode('block');
7374             shim.dom.style.display = 'none';
7375             shim.dom.style.visibility = 'visible';
7376         }
7377         var pn = this.dom.parentNode;
7378         if(shim.dom.parentNode != pn){
7379             pn.insertBefore(shim.dom, this.dom);
7380         }
7381         shim.setStyle('z-index', this.getZIndex()-2);
7382         this.shim = shim;
7383         return shim;
7384     },
7385
7386     hideShim : function(){
7387         if(this.shim){
7388             this.shim.setDisplayed(false);
7389             shims.push(this.shim);
7390             delete this.shim;
7391         }
7392     },
7393
7394     disableShadow : function(){
7395         if(this.shadow){
7396             this.shadowDisabled = true;
7397             this.shadow.hide();
7398             this.lastShadowOffset = this.shadowOffset;
7399             this.shadowOffset = 0;
7400         }
7401     },
7402
7403     enableShadow : function(show){
7404         if(this.shadow){
7405             this.shadowDisabled = false;
7406             this.shadowOffset = this.lastShadowOffset;
7407             delete this.lastShadowOffset;
7408             if(show){
7409                 this.sync(true);
7410             }
7411         }
7412     },
7413
7414     // private
7415     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7416     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7417     sync : function(doShow){
7418         var sw = this.shadow;
7419         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7420             var sh = this.getShim();
7421
7422             var w = this.getWidth(),
7423                 h = this.getHeight();
7424
7425             var l = this.getLeft(true),
7426                 t = this.getTop(true);
7427
7428             if(sw && !this.shadowDisabled){
7429                 if(doShow && !sw.isVisible()){
7430                     sw.show(this);
7431                 }else{
7432                     sw.realign(l, t, w, h);
7433                 }
7434                 if(sh){
7435                     if(doShow){
7436                        sh.show();
7437                     }
7438                     // fit the shim behind the shadow, so it is shimmed too
7439                     var a = sw.adjusts, s = sh.dom.style;
7440                     s.left = (Math.min(l, l+a.l))+"px";
7441                     s.top = (Math.min(t, t+a.t))+"px";
7442                     s.width = (w+a.w)+"px";
7443                     s.height = (h+a.h)+"px";
7444                 }
7445             }else if(sh){
7446                 if(doShow){
7447                    sh.show();
7448                 }
7449                 sh.setSize(w, h);
7450                 sh.setLeftTop(l, t);
7451             }
7452             
7453         }
7454     },
7455
7456     // private
7457     destroy : function(){
7458         this.hideShim();
7459         if(this.shadow){
7460             this.shadow.hide();
7461         }
7462         this.removeAllListeners();
7463         var pn = this.dom.parentNode;
7464         if(pn){
7465             pn.removeChild(this.dom);
7466         }
7467         Roo.Element.uncache(this.id);
7468     },
7469
7470     remove : function(){
7471         this.destroy();
7472     },
7473
7474     // private
7475     beginUpdate : function(){
7476         this.updating = true;
7477     },
7478
7479     // private
7480     endUpdate : function(){
7481         this.updating = false;
7482         this.sync(true);
7483     },
7484
7485     // private
7486     hideUnders : function(negOffset){
7487         if(this.shadow){
7488             this.shadow.hide();
7489         }
7490         this.hideShim();
7491     },
7492
7493     // private
7494     constrainXY : function(){
7495         if(this.constrain){
7496             var vw = Roo.lib.Dom.getViewWidth(),
7497                 vh = Roo.lib.Dom.getViewHeight();
7498             var s = Roo.get(document).getScroll();
7499
7500             var xy = this.getXY();
7501             var x = xy[0], y = xy[1];   
7502             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7503             // only move it if it needs it
7504             var moved = false;
7505             // first validate right/bottom
7506             if((x + w) > vw+s.left){
7507                 x = vw - w - this.shadowOffset;
7508                 moved = true;
7509             }
7510             if((y + h) > vh+s.top){
7511                 y = vh - h - this.shadowOffset;
7512                 moved = true;
7513             }
7514             // then make sure top/left isn't negative
7515             if(x < s.left){
7516                 x = s.left;
7517                 moved = true;
7518             }
7519             if(y < s.top){
7520                 y = s.top;
7521                 moved = true;
7522             }
7523             if(moved){
7524                 if(this.avoidY){
7525                     var ay = this.avoidY;
7526                     if(y <= ay && (y+h) >= ay){
7527                         y = ay-h-5;   
7528                     }
7529                 }
7530                 xy = [x, y];
7531                 this.storeXY(xy);
7532                 supr.setXY.call(this, xy);
7533                 this.sync();
7534             }
7535         }
7536     },
7537
7538     isVisible : function(){
7539         return this.visible;    
7540     },
7541
7542     // private
7543     showAction : function(){
7544         this.visible = true; // track visibility to prevent getStyle calls
7545         if(this.useDisplay === true){
7546             this.setDisplayed("");
7547         }else if(this.lastXY){
7548             supr.setXY.call(this, this.lastXY);
7549         }else if(this.lastLT){
7550             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7551         }
7552     },
7553
7554     // private
7555     hideAction : function(){
7556         this.visible = false;
7557         if(this.useDisplay === true){
7558             this.setDisplayed(false);
7559         }else{
7560             this.setLeftTop(-10000,-10000);
7561         }
7562     },
7563
7564     // overridden Element method
7565     setVisible : function(v, a, d, c, e){
7566         if(v){
7567             this.showAction();
7568         }
7569         if(a && v){
7570             var cb = function(){
7571                 this.sync(true);
7572                 if(c){
7573                     c();
7574                 }
7575             }.createDelegate(this);
7576             supr.setVisible.call(this, true, true, d, cb, e);
7577         }else{
7578             if(!v){
7579                 this.hideUnders(true);
7580             }
7581             var cb = c;
7582             if(a){
7583                 cb = function(){
7584                     this.hideAction();
7585                     if(c){
7586                         c();
7587                     }
7588                 }.createDelegate(this);
7589             }
7590             supr.setVisible.call(this, v, a, d, cb, e);
7591             if(v){
7592                 this.sync(true);
7593             }else if(!a){
7594                 this.hideAction();
7595             }
7596         }
7597     },
7598
7599     storeXY : function(xy){
7600         delete this.lastLT;
7601         this.lastXY = xy;
7602     },
7603
7604     storeLeftTop : function(left, top){
7605         delete this.lastXY;
7606         this.lastLT = [left, top];
7607     },
7608
7609     // private
7610     beforeFx : function(){
7611         this.beforeAction();
7612         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
7613     },
7614
7615     // private
7616     afterFx : function(){
7617         Roo.Layer.superclass.afterFx.apply(this, arguments);
7618         this.sync(this.isVisible());
7619     },
7620
7621     // private
7622     beforeAction : function(){
7623         if(!this.updating && this.shadow){
7624             this.shadow.hide();
7625         }
7626     },
7627
7628     // overridden Element method
7629     setLeft : function(left){
7630         this.storeLeftTop(left, this.getTop(true));
7631         supr.setLeft.apply(this, arguments);
7632         this.sync();
7633     },
7634
7635     setTop : function(top){
7636         this.storeLeftTop(this.getLeft(true), top);
7637         supr.setTop.apply(this, arguments);
7638         this.sync();
7639     },
7640
7641     setLeftTop : function(left, top){
7642         this.storeLeftTop(left, top);
7643         supr.setLeftTop.apply(this, arguments);
7644         this.sync();
7645     },
7646
7647     setXY : function(xy, a, d, c, e){
7648         this.fixDisplay();
7649         this.beforeAction();
7650         this.storeXY(xy);
7651         var cb = this.createCB(c);
7652         supr.setXY.call(this, xy, a, d, cb, e);
7653         if(!a){
7654             cb();
7655         }
7656     },
7657
7658     // private
7659     createCB : function(c){
7660         var el = this;
7661         return function(){
7662             el.constrainXY();
7663             el.sync(true);
7664             if(c){
7665                 c();
7666             }
7667         };
7668     },
7669
7670     // overridden Element method
7671     setX : function(x, a, d, c, e){
7672         this.setXY([x, this.getY()], a, d, c, e);
7673     },
7674
7675     // overridden Element method
7676     setY : function(y, a, d, c, e){
7677         this.setXY([this.getX(), y], a, d, c, e);
7678     },
7679
7680     // overridden Element method
7681     setSize : function(w, h, a, d, c, e){
7682         this.beforeAction();
7683         var cb = this.createCB(c);
7684         supr.setSize.call(this, w, h, a, d, cb, e);
7685         if(!a){
7686             cb();
7687         }
7688     },
7689
7690     // overridden Element method
7691     setWidth : function(w, a, d, c, e){
7692         this.beforeAction();
7693         var cb = this.createCB(c);
7694         supr.setWidth.call(this, w, a, d, cb, e);
7695         if(!a){
7696             cb();
7697         }
7698     },
7699
7700     // overridden Element method
7701     setHeight : function(h, a, d, c, e){
7702         this.beforeAction();
7703         var cb = this.createCB(c);
7704         supr.setHeight.call(this, h, a, d, cb, e);
7705         if(!a){
7706             cb();
7707         }
7708     },
7709
7710     // overridden Element method
7711     setBounds : function(x, y, w, h, a, d, c, e){
7712         this.beforeAction();
7713         var cb = this.createCB(c);
7714         if(!a){
7715             this.storeXY([x, y]);
7716             supr.setXY.call(this, [x, y]);
7717             supr.setSize.call(this, w, h, a, d, cb, e);
7718             cb();
7719         }else{
7720             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
7721         }
7722         return this;
7723     },
7724     
7725     /**
7726      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
7727      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
7728      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
7729      * @param {Number} zindex The new z-index to set
7730      * @return {this} The Layer
7731      */
7732     setZIndex : function(zindex){
7733         this.zindex = zindex;
7734         this.setStyle("z-index", zindex + 2);
7735         if(this.shadow){
7736             this.shadow.setZIndex(zindex + 1);
7737         }
7738         if(this.shim){
7739             this.shim.setStyle("z-index", zindex);
7740         }
7741     }
7742 });
7743 })();/*
7744  * Based on:
7745  * Ext JS Library 1.1.1
7746  * Copyright(c) 2006-2007, Ext JS, LLC.
7747  *
7748  * Originally Released Under LGPL - original licence link has changed is not relivant.
7749  *
7750  * Fork - LGPL
7751  * <script type="text/javascript">
7752  */
7753
7754
7755 /**
7756  * @class Roo.Shadow
7757  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
7758  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
7759  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
7760  * @constructor
7761  * Create a new Shadow
7762  * @param {Object} config The config object
7763  */
7764 Roo.Shadow = function(config){
7765     Roo.apply(this, config);
7766     if(typeof this.mode != "string"){
7767         this.mode = this.defaultMode;
7768     }
7769     var o = this.offset, a = {h: 0};
7770     var rad = Math.floor(this.offset/2);
7771     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
7772         case "drop":
7773             a.w = 0;
7774             a.l = a.t = o;
7775             a.t -= 1;
7776             if(Roo.isIE){
7777                 a.l -= this.offset + rad;
7778                 a.t -= this.offset + rad;
7779                 a.w -= rad;
7780                 a.h -= rad;
7781                 a.t += 1;
7782             }
7783         break;
7784         case "sides":
7785             a.w = (o*2);
7786             a.l = -o;
7787             a.t = o-1;
7788             if(Roo.isIE){
7789                 a.l -= (this.offset - rad);
7790                 a.t -= this.offset + rad;
7791                 a.l += 1;
7792                 a.w -= (this.offset - rad)*2;
7793                 a.w -= rad + 1;
7794                 a.h -= 1;
7795             }
7796         break;
7797         case "frame":
7798             a.w = a.h = (o*2);
7799             a.l = a.t = -o;
7800             a.t += 1;
7801             a.h -= 2;
7802             if(Roo.isIE){
7803                 a.l -= (this.offset - rad);
7804                 a.t -= (this.offset - rad);
7805                 a.l += 1;
7806                 a.w -= (this.offset + rad + 1);
7807                 a.h -= (this.offset + rad);
7808                 a.h += 1;
7809             }
7810         break;
7811     };
7812
7813     this.adjusts = a;
7814 };
7815
7816 Roo.Shadow.prototype = {
7817     /**
7818      * @cfg {String} mode
7819      * The shadow display mode.  Supports the following options:<br />
7820      * sides: Shadow displays on both sides and bottom only<br />
7821      * frame: Shadow displays equally on all four sides<br />
7822      * drop: Traditional bottom-right drop shadow (default)
7823      */
7824     /**
7825      * @cfg {String} offset
7826      * The number of pixels to offset the shadow from the element (defaults to 4)
7827      */
7828     offset: 4,
7829
7830     // private
7831     defaultMode: "drop",
7832
7833     /**
7834      * Displays the shadow under the target element
7835      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
7836      */
7837     show : function(target){
7838         target = Roo.get(target);
7839         if(!this.el){
7840             this.el = Roo.Shadow.Pool.pull();
7841             if(this.el.dom.nextSibling != target.dom){
7842                 this.el.insertBefore(target);
7843             }
7844         }
7845         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
7846         if(Roo.isIE){
7847             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
7848         }
7849         this.realign(
7850             target.getLeft(true),
7851             target.getTop(true),
7852             target.getWidth(),
7853             target.getHeight()
7854         );
7855         this.el.dom.style.display = "block";
7856     },
7857
7858     /**
7859      * Returns true if the shadow is visible, else false
7860      */
7861     isVisible : function(){
7862         return this.el ? true : false;  
7863     },
7864
7865     /**
7866      * Direct alignment when values are already available. Show must be called at least once before
7867      * calling this method to ensure it is initialized.
7868      * @param {Number} left The target element left position
7869      * @param {Number} top The target element top position
7870      * @param {Number} width The target element width
7871      * @param {Number} height The target element height
7872      */
7873     realign : function(l, t, w, h){
7874         if(!this.el){
7875             return;
7876         }
7877         var a = this.adjusts, d = this.el.dom, s = d.style;
7878         var iea = 0;
7879         s.left = (l+a.l)+"px";
7880         s.top = (t+a.t)+"px";
7881         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
7882  
7883         if(s.width != sws || s.height != shs){
7884             s.width = sws;
7885             s.height = shs;
7886             if(!Roo.isIE){
7887                 var cn = d.childNodes;
7888                 var sww = Math.max(0, (sw-12))+"px";
7889                 cn[0].childNodes[1].style.width = sww;
7890                 cn[1].childNodes[1].style.width = sww;
7891                 cn[2].childNodes[1].style.width = sww;
7892                 cn[1].style.height = Math.max(0, (sh-12))+"px";
7893             }
7894         }
7895     },
7896
7897     /**
7898      * Hides this shadow
7899      */
7900     hide : function(){
7901         if(this.el){
7902             this.el.dom.style.display = "none";
7903             Roo.Shadow.Pool.push(this.el);
7904             delete this.el;
7905         }
7906     },
7907
7908     /**
7909      * Adjust the z-index of this shadow
7910      * @param {Number} zindex The new z-index
7911      */
7912     setZIndex : function(z){
7913         this.zIndex = z;
7914         if(this.el){
7915             this.el.setStyle("z-index", z);
7916         }
7917     }
7918 };
7919
7920 // Private utility class that manages the internal Shadow cache
7921 Roo.Shadow.Pool = function(){
7922     var p = [];
7923     var markup = Roo.isIE ?
7924                  '<div class="x-ie-shadow"></div>' :
7925                  '<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>';
7926     return {
7927         pull : function(){
7928             var sh = p.shift();
7929             if(!sh){
7930                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
7931                 sh.autoBoxAdjust = false;
7932             }
7933             return sh;
7934         },
7935
7936         push : function(sh){
7937             p.push(sh);
7938         }
7939     };
7940 }();/*
7941  * Based on:
7942  * Ext JS Library 1.1.1
7943  * Copyright(c) 2006-2007, Ext JS, LLC.
7944  *
7945  * Originally Released Under LGPL - original licence link has changed is not relivant.
7946  *
7947  * Fork - LGPL
7948  * <script type="text/javascript">
7949  */
7950
7951
7952 /**
7953  * @class Roo.SplitBar
7954  * @extends Roo.util.Observable
7955  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
7956  * <br><br>
7957  * Usage:
7958  * <pre><code>
7959 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
7960                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
7961 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
7962 split.minSize = 100;
7963 split.maxSize = 600;
7964 split.animate = true;
7965 split.on('moved', splitterMoved);
7966 </code></pre>
7967  * @constructor
7968  * Create a new SplitBar
7969  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
7970  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
7971  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
7972  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
7973                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
7974                         position of the SplitBar).
7975  */
7976 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
7977     
7978     /** @private */
7979     this.el = Roo.get(dragElement, true);
7980     this.el.dom.unselectable = "on";
7981     /** @private */
7982     this.resizingEl = Roo.get(resizingElement, true);
7983
7984     /**
7985      * @private
7986      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
7987      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
7988      * @type Number
7989      */
7990     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
7991     
7992     /**
7993      * The minimum size of the resizing element. (Defaults to 0)
7994      * @type Number
7995      */
7996     this.minSize = 0;
7997     
7998     /**
7999      * The maximum size of the resizing element. (Defaults to 2000)
8000      * @type Number
8001      */
8002     this.maxSize = 2000;
8003     
8004     /**
8005      * Whether to animate the transition to the new size
8006      * @type Boolean
8007      */
8008     this.animate = false;
8009     
8010     /**
8011      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8012      * @type Boolean
8013      */
8014     this.useShim = false;
8015     
8016     /** @private */
8017     this.shim = null;
8018     
8019     if(!existingProxy){
8020         /** @private */
8021         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8022     }else{
8023         this.proxy = Roo.get(existingProxy).dom;
8024     }
8025     /** @private */
8026     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8027     
8028     /** @private */
8029     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8030     
8031     /** @private */
8032     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8033     
8034     /** @private */
8035     this.dragSpecs = {};
8036     
8037     /**
8038      * @private The adapter to use to positon and resize elements
8039      */
8040     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8041     this.adapter.init(this);
8042     
8043     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8044         /** @private */
8045         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8046         this.el.addClass("x-splitbar-h");
8047     }else{
8048         /** @private */
8049         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8050         this.el.addClass("x-splitbar-v");
8051     }
8052     
8053     this.addEvents({
8054         /**
8055          * @event resize
8056          * Fires when the splitter is moved (alias for {@link #event-moved})
8057          * @param {Roo.SplitBar} this
8058          * @param {Number} newSize the new width or height
8059          */
8060         "resize" : true,
8061         /**
8062          * @event moved
8063          * Fires when the splitter is moved
8064          * @param {Roo.SplitBar} this
8065          * @param {Number} newSize the new width or height
8066          */
8067         "moved" : true,
8068         /**
8069          * @event beforeresize
8070          * Fires before the splitter is dragged
8071          * @param {Roo.SplitBar} this
8072          */
8073         "beforeresize" : true,
8074
8075         "beforeapply" : true
8076     });
8077
8078     Roo.util.Observable.call(this);
8079 };
8080
8081 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8082     onStartProxyDrag : function(x, y){
8083         this.fireEvent("beforeresize", this);
8084         if(!this.overlay){
8085             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8086             o.unselectable();
8087             o.enableDisplayMode("block");
8088             // all splitbars share the same overlay
8089             Roo.SplitBar.prototype.overlay = o;
8090         }
8091         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8092         this.overlay.show();
8093         Roo.get(this.proxy).setDisplayed("block");
8094         var size = this.adapter.getElementSize(this);
8095         this.activeMinSize = this.getMinimumSize();;
8096         this.activeMaxSize = this.getMaximumSize();;
8097         var c1 = size - this.activeMinSize;
8098         var c2 = Math.max(this.activeMaxSize - size, 0);
8099         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8100             this.dd.resetConstraints();
8101             this.dd.setXConstraint(
8102                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8103                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8104             );
8105             this.dd.setYConstraint(0, 0);
8106         }else{
8107             this.dd.resetConstraints();
8108             this.dd.setXConstraint(0, 0);
8109             this.dd.setYConstraint(
8110                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8111                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8112             );
8113          }
8114         this.dragSpecs.startSize = size;
8115         this.dragSpecs.startPoint = [x, y];
8116         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8117     },
8118     
8119     /** 
8120      * @private Called after the drag operation by the DDProxy
8121      */
8122     onEndProxyDrag : function(e){
8123         Roo.get(this.proxy).setDisplayed(false);
8124         var endPoint = Roo.lib.Event.getXY(e);
8125         if(this.overlay){
8126             this.overlay.hide();
8127         }
8128         var newSize;
8129         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8130             newSize = this.dragSpecs.startSize + 
8131                 (this.placement == Roo.SplitBar.LEFT ?
8132                     endPoint[0] - this.dragSpecs.startPoint[0] :
8133                     this.dragSpecs.startPoint[0] - endPoint[0]
8134                 );
8135         }else{
8136             newSize = this.dragSpecs.startSize + 
8137                 (this.placement == Roo.SplitBar.TOP ?
8138                     endPoint[1] - this.dragSpecs.startPoint[1] :
8139                     this.dragSpecs.startPoint[1] - endPoint[1]
8140                 );
8141         }
8142         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8143         if(newSize != this.dragSpecs.startSize){
8144             if(this.fireEvent('beforeapply', this, newSize) !== false){
8145                 this.adapter.setElementSize(this, newSize);
8146                 this.fireEvent("moved", this, newSize);
8147                 this.fireEvent("resize", this, newSize);
8148             }
8149         }
8150     },
8151     
8152     /**
8153      * Get the adapter this SplitBar uses
8154      * @return The adapter object
8155      */
8156     getAdapter : function(){
8157         return this.adapter;
8158     },
8159     
8160     /**
8161      * Set the adapter this SplitBar uses
8162      * @param {Object} adapter A SplitBar adapter object
8163      */
8164     setAdapter : function(adapter){
8165         this.adapter = adapter;
8166         this.adapter.init(this);
8167     },
8168     
8169     /**
8170      * Gets the minimum size for the resizing element
8171      * @return {Number} The minimum size
8172      */
8173     getMinimumSize : function(){
8174         return this.minSize;
8175     },
8176     
8177     /**
8178      * Sets the minimum size for the resizing element
8179      * @param {Number} minSize The minimum size
8180      */
8181     setMinimumSize : function(minSize){
8182         this.minSize = minSize;
8183     },
8184     
8185     /**
8186      * Gets the maximum size for the resizing element
8187      * @return {Number} The maximum size
8188      */
8189     getMaximumSize : function(){
8190         return this.maxSize;
8191     },
8192     
8193     /**
8194      * Sets the maximum size for the resizing element
8195      * @param {Number} maxSize The maximum size
8196      */
8197     setMaximumSize : function(maxSize){
8198         this.maxSize = maxSize;
8199     },
8200     
8201     /**
8202      * Sets the initialize size for the resizing element
8203      * @param {Number} size The initial size
8204      */
8205     setCurrentSize : function(size){
8206         var oldAnimate = this.animate;
8207         this.animate = false;
8208         this.adapter.setElementSize(this, size);
8209         this.animate = oldAnimate;
8210     },
8211     
8212     /**
8213      * Destroy this splitbar. 
8214      * @param {Boolean} removeEl True to remove the element
8215      */
8216     destroy : function(removeEl){
8217         if(this.shim){
8218             this.shim.remove();
8219         }
8220         this.dd.unreg();
8221         this.proxy.parentNode.removeChild(this.proxy);
8222         if(removeEl){
8223             this.el.remove();
8224         }
8225     }
8226 });
8227
8228 /**
8229  * @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.
8230  */
8231 Roo.SplitBar.createProxy = function(dir){
8232     var proxy = new Roo.Element(document.createElement("div"));
8233     proxy.unselectable();
8234     var cls = 'x-splitbar-proxy';
8235     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8236     document.body.appendChild(proxy.dom);
8237     return proxy.dom;
8238 };
8239
8240 /** 
8241  * @class Roo.SplitBar.BasicLayoutAdapter
8242  * Default Adapter. It assumes the splitter and resizing element are not positioned
8243  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8244  */
8245 Roo.SplitBar.BasicLayoutAdapter = function(){
8246 };
8247
8248 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8249     // do nothing for now
8250     init : function(s){
8251     
8252     },
8253     /**
8254      * Called before drag operations to get the current size of the resizing element. 
8255      * @param {Roo.SplitBar} s The SplitBar using this adapter
8256      */
8257      getElementSize : function(s){
8258         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8259             return s.resizingEl.getWidth();
8260         }else{
8261             return s.resizingEl.getHeight();
8262         }
8263     },
8264     
8265     /**
8266      * Called after drag operations to set the size of the resizing element.
8267      * @param {Roo.SplitBar} s The SplitBar using this adapter
8268      * @param {Number} newSize The new size to set
8269      * @param {Function} onComplete A function to be invoked when resizing is complete
8270      */
8271     setElementSize : function(s, newSize, onComplete){
8272         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8273             if(!s.animate){
8274                 s.resizingEl.setWidth(newSize);
8275                 if(onComplete){
8276                     onComplete(s, newSize);
8277                 }
8278             }else{
8279                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8280             }
8281         }else{
8282             
8283             if(!s.animate){
8284                 s.resizingEl.setHeight(newSize);
8285                 if(onComplete){
8286                     onComplete(s, newSize);
8287                 }
8288             }else{
8289                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8290             }
8291         }
8292     }
8293 };
8294
8295 /** 
8296  *@class Roo.SplitBar.AbsoluteLayoutAdapter
8297  * @extends Roo.SplitBar.BasicLayoutAdapter
8298  * Adapter that  moves the splitter element to align with the resized sizing element. 
8299  * Used with an absolute positioned SplitBar.
8300  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
8301  * document.body, make sure you assign an id to the body element.
8302  */
8303 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
8304     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
8305     this.container = Roo.get(container);
8306 };
8307
8308 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
8309     init : function(s){
8310         this.basic.init(s);
8311     },
8312     
8313     getElementSize : function(s){
8314         return this.basic.getElementSize(s);
8315     },
8316     
8317     setElementSize : function(s, newSize, onComplete){
8318         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
8319     },
8320     
8321     moveSplitter : function(s){
8322         var yes = Roo.SplitBar;
8323         switch(s.placement){
8324             case yes.LEFT:
8325                 s.el.setX(s.resizingEl.getRight());
8326                 break;
8327             case yes.RIGHT:
8328                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
8329                 break;
8330             case yes.TOP:
8331                 s.el.setY(s.resizingEl.getBottom());
8332                 break;
8333             case yes.BOTTOM:
8334                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
8335                 break;
8336         }
8337     }
8338 };
8339
8340 /**
8341  * Orientation constant - Create a vertical SplitBar
8342  * @static
8343  * @type Number
8344  */
8345 Roo.SplitBar.VERTICAL = 1;
8346
8347 /**
8348  * Orientation constant - Create a horizontal SplitBar
8349  * @static
8350  * @type Number
8351  */
8352 Roo.SplitBar.HORIZONTAL = 2;
8353
8354 /**
8355  * Placement constant - The resizing element is to the left of the splitter element
8356  * @static
8357  * @type Number
8358  */
8359 Roo.SplitBar.LEFT = 1;
8360
8361 /**
8362  * Placement constant - The resizing element is to the right of the splitter element
8363  * @static
8364  * @type Number
8365  */
8366 Roo.SplitBar.RIGHT = 2;
8367
8368 /**
8369  * Placement constant - The resizing element is positioned above the splitter element
8370  * @static
8371  * @type Number
8372  */
8373 Roo.SplitBar.TOP = 3;
8374
8375 /**
8376  * Placement constant - The resizing element is positioned under splitter element
8377  * @static
8378  * @type Number
8379  */
8380 Roo.SplitBar.BOTTOM = 4;
8381 /*
8382  * Based on:
8383  * Ext JS Library 1.1.1
8384  * Copyright(c) 2006-2007, Ext JS, LLC.
8385  *
8386  * Originally Released Under LGPL - original licence link has changed is not relivant.
8387  *
8388  * Fork - LGPL
8389  * <script type="text/javascript">
8390  */
8391
8392 /**
8393  * @class Roo.View
8394  * @extends Roo.util.Observable
8395  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
8396  * This class also supports single and multi selection modes. <br>
8397  * Create a data model bound view:
8398  <pre><code>
8399  var store = new Roo.data.Store(...);
8400
8401  var view = new Roo.View({
8402     el : "my-element",
8403     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
8404  
8405     singleSelect: true,
8406     selectedClass: "ydataview-selected",
8407     store: store
8408  });
8409
8410  // listen for node click?
8411  view.on("click", function(vw, index, node, e){
8412  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
8413  });
8414
8415  // load XML data
8416  dataModel.load("foobar.xml");
8417  </code></pre>
8418  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
8419  * <br><br>
8420  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
8421  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
8422  * 
8423  * Note: old style constructor is still suported (container, template, config)
8424  * 
8425  * @constructor
8426  * Create a new View
8427  * @param {Object} config The config object
8428  * 
8429  */
8430 Roo.View = function(config, depreciated_tpl, depreciated_config){
8431     
8432     this.parent = false;
8433     
8434     if (typeof(depreciated_tpl) == 'undefined') {
8435         // new way.. - universal constructor.
8436         Roo.apply(this, config);
8437         this.el  = Roo.get(this.el);
8438     } else {
8439         // old format..
8440         this.el  = Roo.get(config);
8441         this.tpl = depreciated_tpl;
8442         Roo.apply(this, depreciated_config);
8443     }
8444     this.wrapEl  = this.el.wrap().wrap();
8445     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
8446     
8447     
8448     if(typeof(this.tpl) == "string"){
8449         this.tpl = new Roo.Template(this.tpl);
8450     } else {
8451         // support xtype ctors..
8452         this.tpl = new Roo.factory(this.tpl, Roo);
8453     }
8454     
8455     
8456     this.tpl.compile();
8457     
8458     /** @private */
8459     this.addEvents({
8460         /**
8461          * @event beforeclick
8462          * Fires before a click is processed. Returns false to cancel the default action.
8463          * @param {Roo.View} this
8464          * @param {Number} index The index of the target node
8465          * @param {HTMLElement} node The target node
8466          * @param {Roo.EventObject} e The raw event object
8467          */
8468             "beforeclick" : true,
8469         /**
8470          * @event click
8471          * Fires when a template node is clicked.
8472          * @param {Roo.View} this
8473          * @param {Number} index The index of the target node
8474          * @param {HTMLElement} node The target node
8475          * @param {Roo.EventObject} e The raw event object
8476          */
8477             "click" : true,
8478         /**
8479          * @event dblclick
8480          * Fires when a template node is double clicked.
8481          * @param {Roo.View} this
8482          * @param {Number} index The index of the target node
8483          * @param {HTMLElement} node The target node
8484          * @param {Roo.EventObject} e The raw event object
8485          */
8486             "dblclick" : true,
8487         /**
8488          * @event contextmenu
8489          * Fires when a template node is right clicked.
8490          * @param {Roo.View} this
8491          * @param {Number} index The index of the target node
8492          * @param {HTMLElement} node The target node
8493          * @param {Roo.EventObject} e The raw event object
8494          */
8495             "contextmenu" : true,
8496         /**
8497          * @event selectionchange
8498          * Fires when the selected nodes change.
8499          * @param {Roo.View} this
8500          * @param {Array} selections Array of the selected nodes
8501          */
8502             "selectionchange" : true,
8503     
8504         /**
8505          * @event beforeselect
8506          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
8507          * @param {Roo.View} this
8508          * @param {HTMLElement} node The node to be selected
8509          * @param {Array} selections Array of currently selected nodes
8510          */
8511             "beforeselect" : true,
8512         /**
8513          * @event preparedata
8514          * Fires on every row to render, to allow you to change the data.
8515          * @param {Roo.View} this
8516          * @param {Object} data to be rendered (change this)
8517          */
8518           "preparedata" : true
8519           
8520           
8521         });
8522
8523
8524
8525     this.el.on({
8526         "click": this.onClick,
8527         "dblclick": this.onDblClick,
8528         "contextmenu": this.onContextMenu,
8529         scope:this
8530     });
8531
8532     this.selections = [];
8533     this.nodes = [];
8534     this.cmp = new Roo.CompositeElementLite([]);
8535     if(this.store){
8536         this.store = Roo.factory(this.store, Roo.data);
8537         this.setStore(this.store, true);
8538     }
8539     
8540     if ( this.footer && this.footer.xtype) {
8541            
8542          var fctr = this.wrapEl.appendChild(document.createElement("div"));
8543         
8544         this.footer.dataSource = this.store;
8545         this.footer.container = fctr;
8546         this.footer = Roo.factory(this.footer, Roo);
8547         fctr.insertFirst(this.el);
8548         
8549         // this is a bit insane - as the paging toolbar seems to detach the el..
8550 //        dom.parentNode.parentNode.parentNode
8551          // they get detached?
8552     }
8553     
8554     
8555     Roo.View.superclass.constructor.call(this);
8556     
8557     
8558 };
8559
8560 Roo.extend(Roo.View, Roo.util.Observable, {
8561     
8562      /**
8563      * @cfg {Roo.data.Store} store Data store to load data from.
8564      */
8565     store : false,
8566     
8567     /**
8568      * @cfg {String|Roo.Element} el The container element.
8569      */
8570     el : '',
8571     
8572     /**
8573      * @cfg {String|Roo.Template} tpl The template used by this View 
8574      */
8575     tpl : false,
8576     /**
8577      * @cfg {String} dataName the named area of the template to use as the data area
8578      *                          Works with domtemplates roo-name="name"
8579      */
8580     dataName: false,
8581     /**
8582      * @cfg {String} selectedClass The css class to add to selected nodes
8583      */
8584     selectedClass : "x-view-selected",
8585      /**
8586      * @cfg {String} emptyText The empty text to show when nothing is loaded.
8587      */
8588     emptyText : "",
8589     
8590     /**
8591      * @cfg {String} text to display on mask (default Loading)
8592      */
8593     mask : false,
8594     /**
8595      * @cfg {Boolean} multiSelect Allow multiple selection
8596      */
8597     multiSelect : false,
8598     /**
8599      * @cfg {Boolean} singleSelect Allow single selection
8600      */
8601     singleSelect:  false,
8602     
8603     /**
8604      * @cfg {Boolean} toggleSelect - selecting 
8605      */
8606     toggleSelect : false,
8607     
8608     /**
8609      * @cfg {Boolean} tickable - selecting 
8610      */
8611     tickable : false,
8612     
8613     /**
8614      * Returns the element this view is bound to.
8615      * @return {Roo.Element}
8616      */
8617     getEl : function(){
8618         return this.wrapEl;
8619     },
8620     
8621     
8622
8623     /**
8624      * Refreshes the view. - called by datachanged on the store. - do not call directly.
8625      */
8626     refresh : function(){
8627         //Roo.log('refresh');
8628         var t = this.tpl;
8629         
8630         // if we are using something like 'domtemplate', then
8631         // the what gets used is:
8632         // t.applySubtemplate(NAME, data, wrapping data..)
8633         // the outer template then get' applied with
8634         //     the store 'extra data'
8635         // and the body get's added to the
8636         //      roo-name="data" node?
8637         //      <span class='roo-tpl-{name}'></span> ?????
8638         
8639         
8640         
8641         this.clearSelections();
8642         this.el.update("");
8643         var html = [];
8644         var records = this.store.getRange();
8645         if(records.length < 1) {
8646             
8647             // is this valid??  = should it render a template??
8648             
8649             this.el.update(this.emptyText);
8650             return;
8651         }
8652         var el = this.el;
8653         if (this.dataName) {
8654             this.el.update(t.apply(this.store.meta)); //????
8655             el = this.el.child('.roo-tpl-' + this.dataName);
8656         }
8657         
8658         for(var i = 0, len = records.length; i < len; i++){
8659             var data = this.prepareData(records[i].data, i, records[i]);
8660             this.fireEvent("preparedata", this, data, i, records[i]);
8661             
8662             var d = Roo.apply({}, data);
8663             
8664             if(this.tickable){
8665                 Roo.apply(d, {'roo-id' : Roo.id()});
8666                 
8667                 var _this = this;
8668             
8669                 Roo.each(this.parent.item, function(item){
8670                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
8671                         return;
8672                     }
8673                     Roo.apply(d, {'roo-data-checked' : 'checked'});
8674                 });
8675             }
8676             
8677             html[html.length] = Roo.util.Format.trim(
8678                 this.dataName ?
8679                     t.applySubtemplate(this.dataName, d, this.store.meta) :
8680                     t.apply(d)
8681             );
8682         }
8683         
8684         
8685         
8686         el.update(html.join(""));
8687         this.nodes = el.dom.childNodes;
8688         this.updateIndexes(0);
8689     },
8690     
8691
8692     /**
8693      * Function to override to reformat the data that is sent to
8694      * the template for each node.
8695      * DEPRICATED - use the preparedata event handler.
8696      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
8697      * a JSON object for an UpdateManager bound view).
8698      */
8699     prepareData : function(data, index, record)
8700     {
8701         this.fireEvent("preparedata", this, data, index, record);
8702         return data;
8703     },
8704
8705     onUpdate : function(ds, record){
8706         // Roo.log('on update');   
8707         this.clearSelections();
8708         var index = this.store.indexOf(record);
8709         var n = this.nodes[index];
8710         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
8711         n.parentNode.removeChild(n);
8712         this.updateIndexes(index, index);
8713     },
8714
8715     
8716     
8717 // --------- FIXME     
8718     onAdd : function(ds, records, index)
8719     {
8720         //Roo.log(['on Add', ds, records, index] );        
8721         this.clearSelections();
8722         if(this.nodes.length == 0){
8723             this.refresh();
8724             return;
8725         }
8726         var n = this.nodes[index];
8727         for(var i = 0, len = records.length; i < len; i++){
8728             var d = this.prepareData(records[i].data, i, records[i]);
8729             if(n){
8730                 this.tpl.insertBefore(n, d);
8731             }else{
8732                 
8733                 this.tpl.append(this.el, d);
8734             }
8735         }
8736         this.updateIndexes(index);
8737     },
8738
8739     onRemove : function(ds, record, index){
8740        // Roo.log('onRemove');
8741         this.clearSelections();
8742         var el = this.dataName  ?
8743             this.el.child('.roo-tpl-' + this.dataName) :
8744             this.el; 
8745         
8746         el.dom.removeChild(this.nodes[index]);
8747         this.updateIndexes(index);
8748     },
8749
8750     /**
8751      * Refresh an individual node.
8752      * @param {Number} index
8753      */
8754     refreshNode : function(index){
8755         this.onUpdate(this.store, this.store.getAt(index));
8756     },
8757
8758     updateIndexes : function(startIndex, endIndex){
8759         var ns = this.nodes;
8760         startIndex = startIndex || 0;
8761         endIndex = endIndex || ns.length - 1;
8762         for(var i = startIndex; i <= endIndex; i++){
8763             ns[i].nodeIndex = i;
8764         }
8765     },
8766
8767     /**
8768      * Changes the data store this view uses and refresh the view.
8769      * @param {Store} store
8770      */
8771     setStore : function(store, initial){
8772         if(!initial && this.store){
8773             this.store.un("datachanged", this.refresh);
8774             this.store.un("add", this.onAdd);
8775             this.store.un("remove", this.onRemove);
8776             this.store.un("update", this.onUpdate);
8777             this.store.un("clear", this.refresh);
8778             this.store.un("beforeload", this.onBeforeLoad);
8779             this.store.un("load", this.onLoad);
8780             this.store.un("loadexception", this.onLoad);
8781         }
8782         if(store){
8783           
8784             store.on("datachanged", this.refresh, this);
8785             store.on("add", this.onAdd, this);
8786             store.on("remove", this.onRemove, this);
8787             store.on("update", this.onUpdate, this);
8788             store.on("clear", this.refresh, this);
8789             store.on("beforeload", this.onBeforeLoad, this);
8790             store.on("load", this.onLoad, this);
8791             store.on("loadexception", this.onLoad, this);
8792         }
8793         
8794         if(store){
8795             this.refresh();
8796         }
8797     },
8798     /**
8799      * onbeforeLoad - masks the loading area.
8800      *
8801      */
8802     onBeforeLoad : function(store,opts)
8803     {
8804          //Roo.log('onBeforeLoad');   
8805         if (!opts.add) {
8806             this.el.update("");
8807         }
8808         this.el.mask(this.mask ? this.mask : "Loading" ); 
8809     },
8810     onLoad : function ()
8811     {
8812         this.el.unmask();
8813     },
8814     
8815
8816     /**
8817      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
8818      * @param {HTMLElement} node
8819      * @return {HTMLElement} The template node
8820      */
8821     findItemFromChild : function(node){
8822         var el = this.dataName  ?
8823             this.el.child('.roo-tpl-' + this.dataName,true) :
8824             this.el.dom; 
8825         
8826         if(!node || node.parentNode == el){
8827                     return node;
8828             }
8829             var p = node.parentNode;
8830             while(p && p != el){
8831             if(p.parentNode == el){
8832                 return p;
8833             }
8834             p = p.parentNode;
8835         }
8836             return null;
8837     },
8838
8839     /** @ignore */
8840     onClick : function(e){
8841         var item = this.findItemFromChild(e.getTarget());
8842         if(item){
8843             var index = this.indexOf(item);
8844             if(this.onItemClick(item, index, e) !== false){
8845                 this.fireEvent("click", this, index, item, e);
8846             }
8847         }else{
8848             this.clearSelections();
8849         }
8850     },
8851
8852     /** @ignore */
8853     onContextMenu : function(e){
8854         var item = this.findItemFromChild(e.getTarget());
8855         if(item){
8856             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
8857         }
8858     },
8859
8860     /** @ignore */
8861     onDblClick : function(e){
8862         var item = this.findItemFromChild(e.getTarget());
8863         if(item){
8864             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
8865         }
8866     },
8867
8868     onItemClick : function(item, index, e)
8869     {
8870         if(this.fireEvent("beforeclick", this, index, item, e) === false){
8871             return false;
8872         }
8873         if (this.toggleSelect) {
8874             var m = this.isSelected(item) ? 'unselect' : 'select';
8875             //Roo.log(m);
8876             var _t = this;
8877             _t[m](item, true, false);
8878             return true;
8879         }
8880         if(this.multiSelect || this.singleSelect){
8881             if(this.multiSelect && e.shiftKey && this.lastSelection){
8882                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
8883             }else{
8884                 this.select(item, this.multiSelect && e.ctrlKey);
8885                 this.lastSelection = item;
8886             }
8887             
8888             if(!this.tickable){
8889                 e.preventDefault();
8890             }
8891             
8892         }
8893         return true;
8894     },
8895
8896     /**
8897      * Get the number of selected nodes.
8898      * @return {Number}
8899      */
8900     getSelectionCount : function(){
8901         return this.selections.length;
8902     },
8903
8904     /**
8905      * Get the currently selected nodes.
8906      * @return {Array} An array of HTMLElements
8907      */
8908     getSelectedNodes : function(){
8909         return this.selections;
8910     },
8911
8912     /**
8913      * Get the indexes of the selected nodes.
8914      * @return {Array}
8915      */
8916     getSelectedIndexes : function(){
8917         var indexes = [], s = this.selections;
8918         for(var i = 0, len = s.length; i < len; i++){
8919             indexes.push(s[i].nodeIndex);
8920         }
8921         return indexes;
8922     },
8923
8924     /**
8925      * Clear all selections
8926      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
8927      */
8928     clearSelections : function(suppressEvent){
8929         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
8930             this.cmp.elements = this.selections;
8931             this.cmp.removeClass(this.selectedClass);
8932             this.selections = [];
8933             if(!suppressEvent){
8934                 this.fireEvent("selectionchange", this, this.selections);
8935             }
8936         }
8937     },
8938
8939     /**
8940      * Returns true if the passed node is selected
8941      * @param {HTMLElement/Number} node The node or node index
8942      * @return {Boolean}
8943      */
8944     isSelected : function(node){
8945         var s = this.selections;
8946         if(s.length < 1){
8947             return false;
8948         }
8949         node = this.getNode(node);
8950         return s.indexOf(node) !== -1;
8951     },
8952
8953     /**
8954      * Selects nodes.
8955      * @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
8956      * @param {Boolean} keepExisting (optional) true to keep existing selections
8957      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
8958      */
8959     select : function(nodeInfo, keepExisting, suppressEvent){
8960         if(nodeInfo instanceof Array){
8961             if(!keepExisting){
8962                 this.clearSelections(true);
8963             }
8964             for(var i = 0, len = nodeInfo.length; i < len; i++){
8965                 this.select(nodeInfo[i], true, true);
8966             }
8967             return;
8968         } 
8969         var node = this.getNode(nodeInfo);
8970         if(!node || this.isSelected(node)){
8971             return; // already selected.
8972         }
8973         if(!keepExisting){
8974             this.clearSelections(true);
8975         }
8976         
8977         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
8978             Roo.fly(node).addClass(this.selectedClass);
8979             this.selections.push(node);
8980             if(!suppressEvent){
8981                 this.fireEvent("selectionchange", this, this.selections);
8982             }
8983         }
8984         
8985         
8986     },
8987       /**
8988      * Unselects nodes.
8989      * @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
8990      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
8991      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
8992      */
8993     unselect : function(nodeInfo, keepExisting, suppressEvent)
8994     {
8995         if(nodeInfo instanceof Array){
8996             Roo.each(this.selections, function(s) {
8997                 this.unselect(s, nodeInfo);
8998             }, this);
8999             return;
9000         }
9001         var node = this.getNode(nodeInfo);
9002         if(!node || !this.isSelected(node)){
9003             //Roo.log("not selected");
9004             return; // not selected.
9005         }
9006         // fireevent???
9007         var ns = [];
9008         Roo.each(this.selections, function(s) {
9009             if (s == node ) {
9010                 Roo.fly(node).removeClass(this.selectedClass);
9011
9012                 return;
9013             }
9014             ns.push(s);
9015         },this);
9016         
9017         this.selections= ns;
9018         this.fireEvent("selectionchange", this, this.selections);
9019     },
9020
9021     /**
9022      * Gets a template node.
9023      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9024      * @return {HTMLElement} The node or null if it wasn't found
9025      */
9026     getNode : function(nodeInfo){
9027         if(typeof nodeInfo == "string"){
9028             return document.getElementById(nodeInfo);
9029         }else if(typeof nodeInfo == "number"){
9030             return this.nodes[nodeInfo];
9031         }
9032         return nodeInfo;
9033     },
9034
9035     /**
9036      * Gets a range template nodes.
9037      * @param {Number} startIndex
9038      * @param {Number} endIndex
9039      * @return {Array} An array of nodes
9040      */
9041     getNodes : function(start, end){
9042         var ns = this.nodes;
9043         start = start || 0;
9044         end = typeof end == "undefined" ? ns.length - 1 : end;
9045         var nodes = [];
9046         if(start <= end){
9047             for(var i = start; i <= end; i++){
9048                 nodes.push(ns[i]);
9049             }
9050         } else{
9051             for(var i = start; i >= end; i--){
9052                 nodes.push(ns[i]);
9053             }
9054         }
9055         return nodes;
9056     },
9057
9058     /**
9059      * Finds the index of the passed node
9060      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9061      * @return {Number} The index of the node or -1
9062      */
9063     indexOf : function(node){
9064         node = this.getNode(node);
9065         if(typeof node.nodeIndex == "number"){
9066             return node.nodeIndex;
9067         }
9068         var ns = this.nodes;
9069         for(var i = 0, len = ns.length; i < len; i++){
9070             if(ns[i] == node){
9071                 return i;
9072             }
9073         }
9074         return -1;
9075     }
9076 });
9077 /*
9078  * Based on:
9079  * Ext JS Library 1.1.1
9080  * Copyright(c) 2006-2007, Ext JS, LLC.
9081  *
9082  * Originally Released Under LGPL - original licence link has changed is not relivant.
9083  *
9084  * Fork - LGPL
9085  * <script type="text/javascript">
9086  */
9087
9088 /**
9089  * @class Roo.JsonView
9090  * @extends Roo.View
9091  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9092 <pre><code>
9093 var view = new Roo.JsonView({
9094     container: "my-element",
9095     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9096     multiSelect: true, 
9097     jsonRoot: "data" 
9098 });
9099
9100 // listen for node click?
9101 view.on("click", function(vw, index, node, e){
9102     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9103 });
9104
9105 // direct load of JSON data
9106 view.load("foobar.php");
9107
9108 // Example from my blog list
9109 var tpl = new Roo.Template(
9110     '&lt;div class="entry"&gt;' +
9111     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9112     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9113     "&lt;/div&gt;&lt;hr /&gt;"
9114 );
9115
9116 var moreView = new Roo.JsonView({
9117     container :  "entry-list", 
9118     template : tpl,
9119     jsonRoot: "posts"
9120 });
9121 moreView.on("beforerender", this.sortEntries, this);
9122 moreView.load({
9123     url: "/blog/get-posts.php",
9124     params: "allposts=true",
9125     text: "Loading Blog Entries..."
9126 });
9127 </code></pre>
9128
9129 * Note: old code is supported with arguments : (container, template, config)
9130
9131
9132  * @constructor
9133  * Create a new JsonView
9134  * 
9135  * @param {Object} config The config object
9136  * 
9137  */
9138 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9139     
9140     
9141     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9142
9143     var um = this.el.getUpdateManager();
9144     um.setRenderer(this);
9145     um.on("update", this.onLoad, this);
9146     um.on("failure", this.onLoadException, this);
9147
9148     /**
9149      * @event beforerender
9150      * Fires before rendering of the downloaded JSON data.
9151      * @param {Roo.JsonView} this
9152      * @param {Object} data The JSON data loaded
9153      */
9154     /**
9155      * @event load
9156      * Fires when data is loaded.
9157      * @param {Roo.JsonView} this
9158      * @param {Object} data The JSON data loaded
9159      * @param {Object} response The raw Connect response object
9160      */
9161     /**
9162      * @event loadexception
9163      * Fires when loading fails.
9164      * @param {Roo.JsonView} this
9165      * @param {Object} response The raw Connect response object
9166      */
9167     this.addEvents({
9168         'beforerender' : true,
9169         'load' : true,
9170         'loadexception' : true
9171     });
9172 };
9173 Roo.extend(Roo.JsonView, Roo.View, {
9174     /**
9175      * @type {String} The root property in the loaded JSON object that contains the data
9176      */
9177     jsonRoot : "",
9178
9179     /**
9180      * Refreshes the view.
9181      */
9182     refresh : function(){
9183         this.clearSelections();
9184         this.el.update("");
9185         var html = [];
9186         var o = this.jsonData;
9187         if(o && o.length > 0){
9188             for(var i = 0, len = o.length; i < len; i++){
9189                 var data = this.prepareData(o[i], i, o);
9190                 html[html.length] = this.tpl.apply(data);
9191             }
9192         }else{
9193             html.push(this.emptyText);
9194         }
9195         this.el.update(html.join(""));
9196         this.nodes = this.el.dom.childNodes;
9197         this.updateIndexes(0);
9198     },
9199
9200     /**
9201      * 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.
9202      * @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:
9203      <pre><code>
9204      view.load({
9205          url: "your-url.php",
9206          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9207          callback: yourFunction,
9208          scope: yourObject, //(optional scope)
9209          discardUrl: false,
9210          nocache: false,
9211          text: "Loading...",
9212          timeout: 30,
9213          scripts: false
9214      });
9215      </code></pre>
9216      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9217      * 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.
9218      * @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}
9219      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9220      * @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.
9221      */
9222     load : function(){
9223         var um = this.el.getUpdateManager();
9224         um.update.apply(um, arguments);
9225     },
9226
9227     render : function(el, response){
9228         this.clearSelections();
9229         this.el.update("");
9230         var o;
9231         try{
9232             o = Roo.util.JSON.decode(response.responseText);
9233             if(this.jsonRoot){
9234                 
9235                 o = o[this.jsonRoot];
9236             }
9237         } catch(e){
9238         }
9239         /**
9240          * The current JSON data or null
9241          */
9242         this.jsonData = o;
9243         this.beforeRender();
9244         this.refresh();
9245     },
9246
9247 /**
9248  * Get the number of records in the current JSON dataset
9249  * @return {Number}
9250  */
9251     getCount : function(){
9252         return this.jsonData ? this.jsonData.length : 0;
9253     },
9254
9255 /**
9256  * Returns the JSON object for the specified node(s)
9257  * @param {HTMLElement/Array} node The node or an array of nodes
9258  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9259  * you get the JSON object for the node
9260  */
9261     getNodeData : function(node){
9262         if(node instanceof Array){
9263             var data = [];
9264             for(var i = 0, len = node.length; i < len; i++){
9265                 data.push(this.getNodeData(node[i]));
9266             }
9267             return data;
9268         }
9269         return this.jsonData[this.indexOf(node)] || null;
9270     },
9271
9272     beforeRender : function(){
9273         this.snapshot = this.jsonData;
9274         if(this.sortInfo){
9275             this.sort.apply(this, this.sortInfo);
9276         }
9277         this.fireEvent("beforerender", this, this.jsonData);
9278     },
9279
9280     onLoad : function(el, o){
9281         this.fireEvent("load", this, this.jsonData, o);
9282     },
9283
9284     onLoadException : function(el, o){
9285         this.fireEvent("loadexception", this, o);
9286     },
9287
9288 /**
9289  * Filter the data by a specific property.
9290  * @param {String} property A property on your JSON objects
9291  * @param {String/RegExp} value Either string that the property values
9292  * should start with, or a RegExp to test against the property
9293  */
9294     filter : function(property, value){
9295         if(this.jsonData){
9296             var data = [];
9297             var ss = this.snapshot;
9298             if(typeof value == "string"){
9299                 var vlen = value.length;
9300                 if(vlen == 0){
9301                     this.clearFilter();
9302                     return;
9303                 }
9304                 value = value.toLowerCase();
9305                 for(var i = 0, len = ss.length; i < len; i++){
9306                     var o = ss[i];
9307                     if(o[property].substr(0, vlen).toLowerCase() == value){
9308                         data.push(o);
9309                     }
9310                 }
9311             } else if(value.exec){ // regex?
9312                 for(var i = 0, len = ss.length; i < len; i++){
9313                     var o = ss[i];
9314                     if(value.test(o[property])){
9315                         data.push(o);
9316                     }
9317                 }
9318             } else{
9319                 return;
9320             }
9321             this.jsonData = data;
9322             this.refresh();
9323         }
9324     },
9325
9326 /**
9327  * Filter by a function. The passed function will be called with each
9328  * object in the current dataset. If the function returns true the value is kept,
9329  * otherwise it is filtered.
9330  * @param {Function} fn
9331  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9332  */
9333     filterBy : function(fn, scope){
9334         if(this.jsonData){
9335             var data = [];
9336             var ss = this.snapshot;
9337             for(var i = 0, len = ss.length; i < len; i++){
9338                 var o = ss[i];
9339                 if(fn.call(scope || this, o)){
9340                     data.push(o);
9341                 }
9342             }
9343             this.jsonData = data;
9344             this.refresh();
9345         }
9346     },
9347
9348 /**
9349  * Clears the current filter.
9350  */
9351     clearFilter : function(){
9352         if(this.snapshot && this.jsonData != this.snapshot){
9353             this.jsonData = this.snapshot;
9354             this.refresh();
9355         }
9356     },
9357
9358
9359 /**
9360  * Sorts the data for this view and refreshes it.
9361  * @param {String} property A property on your JSON objects to sort on
9362  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9363  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9364  */
9365     sort : function(property, dir, sortType){
9366         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9367         if(this.jsonData){
9368             var p = property;
9369             var dsc = dir && dir.toLowerCase() == "desc";
9370             var f = function(o1, o2){
9371                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9372                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9373                 ;
9374                 if(v1 < v2){
9375                     return dsc ? +1 : -1;
9376                 } else if(v1 > v2){
9377                     return dsc ? -1 : +1;
9378                 } else{
9379                     return 0;
9380                 }
9381             };
9382             this.jsonData.sort(f);
9383             this.refresh();
9384             if(this.jsonData != this.snapshot){
9385                 this.snapshot.sort(f);
9386             }
9387         }
9388     }
9389 });/*
9390  * Based on:
9391  * Ext JS Library 1.1.1
9392  * Copyright(c) 2006-2007, Ext JS, LLC.
9393  *
9394  * Originally Released Under LGPL - original licence link has changed is not relivant.
9395  *
9396  * Fork - LGPL
9397  * <script type="text/javascript">
9398  */
9399  
9400
9401 /**
9402  * @class Roo.ColorPalette
9403  * @extends Roo.Component
9404  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9405  * Here's an example of typical usage:
9406  * <pre><code>
9407 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9408 cp.render('my-div');
9409
9410 cp.on('select', function(palette, selColor){
9411     // do something with selColor
9412 });
9413 </code></pre>
9414  * @constructor
9415  * Create a new ColorPalette
9416  * @param {Object} config The config object
9417  */
9418 Roo.ColorPalette = function(config){
9419     Roo.ColorPalette.superclass.constructor.call(this, config);
9420     this.addEvents({
9421         /**
9422              * @event select
9423              * Fires when a color is selected
9424              * @param {ColorPalette} this
9425              * @param {String} color The 6-digit color hex code (without the # symbol)
9426              */
9427         select: true
9428     });
9429
9430     if(this.handler){
9431         this.on("select", this.handler, this.scope, true);
9432     }
9433 };
9434 Roo.extend(Roo.ColorPalette, Roo.Component, {
9435     /**
9436      * @cfg {String} itemCls
9437      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9438      */
9439     itemCls : "x-color-palette",
9440     /**
9441      * @cfg {String} value
9442      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9443      * the hex codes are case-sensitive.
9444      */
9445     value : null,
9446     clickEvent:'click',
9447     // private
9448     ctype: "Roo.ColorPalette",
9449
9450     /**
9451      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9452      */
9453     allowReselect : false,
9454
9455     /**
9456      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9457      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9458      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9459      * of colors with the width setting until the box is symmetrical.</p>
9460      * <p>You can override individual colors if needed:</p>
9461      * <pre><code>
9462 var cp = new Roo.ColorPalette();
9463 cp.colors[0] = "FF0000";  // change the first box to red
9464 </code></pre>
9465
9466 Or you can provide a custom array of your own for complete control:
9467 <pre><code>
9468 var cp = new Roo.ColorPalette();
9469 cp.colors = ["000000", "993300", "333300"];
9470 </code></pre>
9471      * @type Array
9472      */
9473     colors : [
9474         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9475         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9476         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9477         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9478         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9479     ],
9480
9481     // private
9482     onRender : function(container, position){
9483         var t = new Roo.MasterTemplate(
9484             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
9485         );
9486         var c = this.colors;
9487         for(var i = 0, len = c.length; i < len; i++){
9488             t.add([c[i]]);
9489         }
9490         var el = document.createElement("div");
9491         el.className = this.itemCls;
9492         t.overwrite(el);
9493         container.dom.insertBefore(el, position);
9494         this.el = Roo.get(el);
9495         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
9496         if(this.clickEvent != 'click'){
9497             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
9498         }
9499     },
9500
9501     // private
9502     afterRender : function(){
9503         Roo.ColorPalette.superclass.afterRender.call(this);
9504         if(this.value){
9505             var s = this.value;
9506             this.value = null;
9507             this.select(s);
9508         }
9509     },
9510
9511     // private
9512     handleClick : function(e, t){
9513         e.preventDefault();
9514         if(!this.disabled){
9515             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
9516             this.select(c.toUpperCase());
9517         }
9518     },
9519
9520     /**
9521      * Selects the specified color in the palette (fires the select event)
9522      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
9523      */
9524     select : function(color){
9525         color = color.replace("#", "");
9526         if(color != this.value || this.allowReselect){
9527             var el = this.el;
9528             if(this.value){
9529                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
9530             }
9531             el.child("a.color-"+color).addClass("x-color-palette-sel");
9532             this.value = color;
9533             this.fireEvent("select", this, color);
9534         }
9535     }
9536 });/*
9537  * Based on:
9538  * Ext JS Library 1.1.1
9539  * Copyright(c) 2006-2007, Ext JS, LLC.
9540  *
9541  * Originally Released Under LGPL - original licence link has changed is not relivant.
9542  *
9543  * Fork - LGPL
9544  * <script type="text/javascript">
9545  */
9546  
9547 /**
9548  * @class Roo.DatePicker
9549  * @extends Roo.Component
9550  * Simple date picker class.
9551  * @constructor
9552  * Create a new DatePicker
9553  * @param {Object} config The config object
9554  */
9555 Roo.DatePicker = function(config){
9556     Roo.DatePicker.superclass.constructor.call(this, config);
9557
9558     this.value = config && config.value ?
9559                  config.value.clearTime() : new Date().clearTime();
9560
9561     this.addEvents({
9562         /**
9563              * @event select
9564              * Fires when a date is selected
9565              * @param {DatePicker} this
9566              * @param {Date} date The selected date
9567              */
9568         'select': true,
9569         /**
9570              * @event monthchange
9571              * Fires when the displayed month changes 
9572              * @param {DatePicker} this
9573              * @param {Date} date The selected month
9574              */
9575         'monthchange': true
9576     });
9577
9578     if(this.handler){
9579         this.on("select", this.handler,  this.scope || this);
9580     }
9581     // build the disabledDatesRE
9582     if(!this.disabledDatesRE && this.disabledDates){
9583         var dd = this.disabledDates;
9584         var re = "(?:";
9585         for(var i = 0; i < dd.length; i++){
9586             re += dd[i];
9587             if(i != dd.length-1) re += "|";
9588         }
9589         this.disabledDatesRE = new RegExp(re + ")");
9590     }
9591 };
9592
9593 Roo.extend(Roo.DatePicker, Roo.Component, {
9594     /**
9595      * @cfg {String} todayText
9596      * The text to display on the button that selects the current date (defaults to "Today")
9597      */
9598     todayText : "Today",
9599     /**
9600      * @cfg {String} okText
9601      * The text to display on the ok button
9602      */
9603     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
9604     /**
9605      * @cfg {String} cancelText
9606      * The text to display on the cancel button
9607      */
9608     cancelText : "Cancel",
9609     /**
9610      * @cfg {String} todayTip
9611      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
9612      */
9613     todayTip : "{0} (Spacebar)",
9614     /**
9615      * @cfg {Date} minDate
9616      * Minimum allowable date (JavaScript date object, defaults to null)
9617      */
9618     minDate : null,
9619     /**
9620      * @cfg {Date} maxDate
9621      * Maximum allowable date (JavaScript date object, defaults to null)
9622      */
9623     maxDate : null,
9624     /**
9625      * @cfg {String} minText
9626      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
9627      */
9628     minText : "This date is before the minimum date",
9629     /**
9630      * @cfg {String} maxText
9631      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
9632      */
9633     maxText : "This date is after the maximum date",
9634     /**
9635      * @cfg {String} format
9636      * The default date format string which can be overriden for localization support.  The format must be
9637      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
9638      */
9639     format : "m/d/y",
9640     /**
9641      * @cfg {Array} disabledDays
9642      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
9643      */
9644     disabledDays : null,
9645     /**
9646      * @cfg {String} disabledDaysText
9647      * The tooltip to display when the date falls on a disabled day (defaults to "")
9648      */
9649     disabledDaysText : "",
9650     /**
9651      * @cfg {RegExp} disabledDatesRE
9652      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
9653      */
9654     disabledDatesRE : null,
9655     /**
9656      * @cfg {String} disabledDatesText
9657      * The tooltip text to display when the date falls on a disabled date (defaults to "")
9658      */
9659     disabledDatesText : "",
9660     /**
9661      * @cfg {Boolean} constrainToViewport
9662      * True to constrain the date picker to the viewport (defaults to true)
9663      */
9664     constrainToViewport : true,
9665     /**
9666      * @cfg {Array} monthNames
9667      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
9668      */
9669     monthNames : Date.monthNames,
9670     /**
9671      * @cfg {Array} dayNames
9672      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
9673      */
9674     dayNames : Date.dayNames,
9675     /**
9676      * @cfg {String} nextText
9677      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
9678      */
9679     nextText: 'Next Month (Control+Right)',
9680     /**
9681      * @cfg {String} prevText
9682      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
9683      */
9684     prevText: 'Previous Month (Control+Left)',
9685     /**
9686      * @cfg {String} monthYearText
9687      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
9688      */
9689     monthYearText: 'Choose a month (Control+Up/Down to move years)',
9690     /**
9691      * @cfg {Number} startDay
9692      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9693      */
9694     startDay : 0,
9695     /**
9696      * @cfg {Bool} showClear
9697      * Show a clear button (usefull for date form elements that can be blank.)
9698      */
9699     
9700     showClear: false,
9701     
9702     /**
9703      * Sets the value of the date field
9704      * @param {Date} value The date to set
9705      */
9706     setValue : function(value){
9707         var old = this.value;
9708         
9709         if (typeof(value) == 'string') {
9710          
9711             value = Date.parseDate(value, this.format);
9712         }
9713         if (!value) {
9714             value = new Date();
9715         }
9716         
9717         this.value = value.clearTime(true);
9718         if(this.el){
9719             this.update(this.value);
9720         }
9721     },
9722
9723     /**
9724      * Gets the current selected value of the date field
9725      * @return {Date} The selected date
9726      */
9727     getValue : function(){
9728         return this.value;
9729     },
9730
9731     // private
9732     focus : function(){
9733         if(this.el){
9734             this.update(this.activeDate);
9735         }
9736     },
9737
9738     // privateval
9739     onRender : function(container, position){
9740         
9741         var m = [
9742              '<table cellspacing="0">',
9743                 '<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>',
9744                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
9745         var dn = this.dayNames;
9746         for(var i = 0; i < 7; i++){
9747             var d = this.startDay+i;
9748             if(d > 6){
9749                 d = d-7;
9750             }
9751             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
9752         }
9753         m[m.length] = "</tr></thead><tbody><tr>";
9754         for(var i = 0; i < 42; i++) {
9755             if(i % 7 == 0 && i != 0){
9756                 m[m.length] = "</tr><tr>";
9757             }
9758             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
9759         }
9760         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
9761             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
9762
9763         var el = document.createElement("div");
9764         el.className = "x-date-picker";
9765         el.innerHTML = m.join("");
9766
9767         container.dom.insertBefore(el, position);
9768
9769         this.el = Roo.get(el);
9770         this.eventEl = Roo.get(el.firstChild);
9771
9772         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
9773             handler: this.showPrevMonth,
9774             scope: this,
9775             preventDefault:true,
9776             stopDefault:true
9777         });
9778
9779         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
9780             handler: this.showNextMonth,
9781             scope: this,
9782             preventDefault:true,
9783             stopDefault:true
9784         });
9785
9786         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
9787
9788         this.monthPicker = this.el.down('div.x-date-mp');
9789         this.monthPicker.enableDisplayMode('block');
9790         
9791         var kn = new Roo.KeyNav(this.eventEl, {
9792             "left" : function(e){
9793                 e.ctrlKey ?
9794                     this.showPrevMonth() :
9795                     this.update(this.activeDate.add("d", -1));
9796             },
9797
9798             "right" : function(e){
9799                 e.ctrlKey ?
9800                     this.showNextMonth() :
9801                     this.update(this.activeDate.add("d", 1));
9802             },
9803
9804             "up" : function(e){
9805                 e.ctrlKey ?
9806                     this.showNextYear() :
9807                     this.update(this.activeDate.add("d", -7));
9808             },
9809
9810             "down" : function(e){
9811                 e.ctrlKey ?
9812                     this.showPrevYear() :
9813                     this.update(this.activeDate.add("d", 7));
9814             },
9815
9816             "pageUp" : function(e){
9817                 this.showNextMonth();
9818             },
9819
9820             "pageDown" : function(e){
9821                 this.showPrevMonth();
9822             },
9823
9824             "enter" : function(e){
9825                 e.stopPropagation();
9826                 return true;
9827             },
9828
9829             scope : this
9830         });
9831
9832         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
9833
9834         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
9835
9836         this.el.unselectable();
9837         
9838         this.cells = this.el.select("table.x-date-inner tbody td");
9839         this.textNodes = this.el.query("table.x-date-inner tbody span");
9840
9841         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
9842             text: "&#160;",
9843             tooltip: this.monthYearText
9844         });
9845
9846         this.mbtn.on('click', this.showMonthPicker, this);
9847         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
9848
9849
9850         var today = (new Date()).dateFormat(this.format);
9851         
9852         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
9853         if (this.showClear) {
9854             baseTb.add( new Roo.Toolbar.Fill());
9855         }
9856         baseTb.add({
9857             text: String.format(this.todayText, today),
9858             tooltip: String.format(this.todayTip, today),
9859             handler: this.selectToday,
9860             scope: this
9861         });
9862         
9863         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
9864             
9865         //});
9866         if (this.showClear) {
9867             
9868             baseTb.add( new Roo.Toolbar.Fill());
9869             baseTb.add({
9870                 text: '&#160;',
9871                 cls: 'x-btn-icon x-btn-clear',
9872                 handler: function() {
9873                     //this.value = '';
9874                     this.fireEvent("select", this, '');
9875                 },
9876                 scope: this
9877             });
9878         }
9879         
9880         
9881         if(Roo.isIE){
9882             this.el.repaint();
9883         }
9884         this.update(this.value);
9885     },
9886
9887     createMonthPicker : function(){
9888         if(!this.monthPicker.dom.firstChild){
9889             var buf = ['<table border="0" cellspacing="0">'];
9890             for(var i = 0; i < 6; i++){
9891                 buf.push(
9892                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
9893                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
9894                     i == 0 ?
9895                     '<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>' :
9896                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
9897                 );
9898             }
9899             buf.push(
9900                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
9901                     this.okText,
9902                     '</button><button type="button" class="x-date-mp-cancel">',
9903                     this.cancelText,
9904                     '</button></td></tr>',
9905                 '</table>'
9906             );
9907             this.monthPicker.update(buf.join(''));
9908             this.monthPicker.on('click', this.onMonthClick, this);
9909             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
9910
9911             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
9912             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
9913
9914             this.mpMonths.each(function(m, a, i){
9915                 i += 1;
9916                 if((i%2) == 0){
9917                     m.dom.xmonth = 5 + Math.round(i * .5);
9918                 }else{
9919                     m.dom.xmonth = Math.round((i-1) * .5);
9920                 }
9921             });
9922         }
9923     },
9924
9925     showMonthPicker : function(){
9926         this.createMonthPicker();
9927         var size = this.el.getSize();
9928         this.monthPicker.setSize(size);
9929         this.monthPicker.child('table').setSize(size);
9930
9931         this.mpSelMonth = (this.activeDate || this.value).getMonth();
9932         this.updateMPMonth(this.mpSelMonth);
9933         this.mpSelYear = (this.activeDate || this.value).getFullYear();
9934         this.updateMPYear(this.mpSelYear);
9935
9936         this.monthPicker.slideIn('t', {duration:.2});
9937     },
9938
9939     updateMPYear : function(y){
9940         this.mpyear = y;
9941         var ys = this.mpYears.elements;
9942         for(var i = 1; i <= 10; i++){
9943             var td = ys[i-1], y2;
9944             if((i%2) == 0){
9945                 y2 = y + Math.round(i * .5);
9946                 td.firstChild.innerHTML = y2;
9947                 td.xyear = y2;
9948             }else{
9949                 y2 = y - (5-Math.round(i * .5));
9950                 td.firstChild.innerHTML = y2;
9951                 td.xyear = y2;
9952             }
9953             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
9954         }
9955     },
9956
9957     updateMPMonth : function(sm){
9958         this.mpMonths.each(function(m, a, i){
9959             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
9960         });
9961     },
9962
9963     selectMPMonth: function(m){
9964         
9965     },
9966
9967     onMonthClick : function(e, t){
9968         e.stopEvent();
9969         var el = new Roo.Element(t), pn;
9970         if(el.is('button.x-date-mp-cancel')){
9971             this.hideMonthPicker();
9972         }
9973         else if(el.is('button.x-date-mp-ok')){
9974             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
9975             this.hideMonthPicker();
9976         }
9977         else if(pn = el.up('td.x-date-mp-month', 2)){
9978             this.mpMonths.removeClass('x-date-mp-sel');
9979             pn.addClass('x-date-mp-sel');
9980             this.mpSelMonth = pn.dom.xmonth;
9981         }
9982         else if(pn = el.up('td.x-date-mp-year', 2)){
9983             this.mpYears.removeClass('x-date-mp-sel');
9984             pn.addClass('x-date-mp-sel');
9985             this.mpSelYear = pn.dom.xyear;
9986         }
9987         else if(el.is('a.x-date-mp-prev')){
9988             this.updateMPYear(this.mpyear-10);
9989         }
9990         else if(el.is('a.x-date-mp-next')){
9991             this.updateMPYear(this.mpyear+10);
9992         }
9993     },
9994
9995     onMonthDblClick : function(e, t){
9996         e.stopEvent();
9997         var el = new Roo.Element(t), pn;
9998         if(pn = el.up('td.x-date-mp-month', 2)){
9999             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10000             this.hideMonthPicker();
10001         }
10002         else if(pn = el.up('td.x-date-mp-year', 2)){
10003             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10004             this.hideMonthPicker();
10005         }
10006     },
10007
10008     hideMonthPicker : function(disableAnim){
10009         if(this.monthPicker){
10010             if(disableAnim === true){
10011                 this.monthPicker.hide();
10012             }else{
10013                 this.monthPicker.slideOut('t', {duration:.2});
10014             }
10015         }
10016     },
10017
10018     // private
10019     showPrevMonth : function(e){
10020         this.update(this.activeDate.add("mo", -1));
10021     },
10022
10023     // private
10024     showNextMonth : function(e){
10025         this.update(this.activeDate.add("mo", 1));
10026     },
10027
10028     // private
10029     showPrevYear : function(){
10030         this.update(this.activeDate.add("y", -1));
10031     },
10032
10033     // private
10034     showNextYear : function(){
10035         this.update(this.activeDate.add("y", 1));
10036     },
10037
10038     // private
10039     handleMouseWheel : function(e){
10040         var delta = e.getWheelDelta();
10041         if(delta > 0){
10042             this.showPrevMonth();
10043             e.stopEvent();
10044         } else if(delta < 0){
10045             this.showNextMonth();
10046             e.stopEvent();
10047         }
10048     },
10049
10050     // private
10051     handleDateClick : function(e, t){
10052         e.stopEvent();
10053         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10054             this.setValue(new Date(t.dateValue));
10055             this.fireEvent("select", this, this.value);
10056         }
10057     },
10058
10059     // private
10060     selectToday : function(){
10061         this.setValue(new Date().clearTime());
10062         this.fireEvent("select", this, this.value);
10063     },
10064
10065     // private
10066     update : function(date)
10067     {
10068         var vd = this.activeDate;
10069         this.activeDate = date;
10070         if(vd && this.el){
10071             var t = date.getTime();
10072             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10073                 this.cells.removeClass("x-date-selected");
10074                 this.cells.each(function(c){
10075                    if(c.dom.firstChild.dateValue == t){
10076                        c.addClass("x-date-selected");
10077                        setTimeout(function(){
10078                             try{c.dom.firstChild.focus();}catch(e){}
10079                        }, 50);
10080                        return false;
10081                    }
10082                 });
10083                 return;
10084             }
10085         }
10086         
10087         var days = date.getDaysInMonth();
10088         var firstOfMonth = date.getFirstDateOfMonth();
10089         var startingPos = firstOfMonth.getDay()-this.startDay;
10090
10091         if(startingPos <= this.startDay){
10092             startingPos += 7;
10093         }
10094
10095         var pm = date.add("mo", -1);
10096         var prevStart = pm.getDaysInMonth()-startingPos;
10097
10098         var cells = this.cells.elements;
10099         var textEls = this.textNodes;
10100         days += startingPos;
10101
10102         // convert everything to numbers so it's fast
10103         var day = 86400000;
10104         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10105         var today = new Date().clearTime().getTime();
10106         var sel = date.clearTime().getTime();
10107         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10108         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10109         var ddMatch = this.disabledDatesRE;
10110         var ddText = this.disabledDatesText;
10111         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10112         var ddaysText = this.disabledDaysText;
10113         var format = this.format;
10114
10115         var setCellClass = function(cal, cell){
10116             cell.title = "";
10117             var t = d.getTime();
10118             cell.firstChild.dateValue = t;
10119             if(t == today){
10120                 cell.className += " x-date-today";
10121                 cell.title = cal.todayText;
10122             }
10123             if(t == sel){
10124                 cell.className += " x-date-selected";
10125                 setTimeout(function(){
10126                     try{cell.firstChild.focus();}catch(e){}
10127                 }, 50);
10128             }
10129             // disabling
10130             if(t < min) {
10131                 cell.className = " x-date-disabled";
10132                 cell.title = cal.minText;
10133                 return;
10134             }
10135             if(t > max) {
10136                 cell.className = " x-date-disabled";
10137                 cell.title = cal.maxText;
10138                 return;
10139             }
10140             if(ddays){
10141                 if(ddays.indexOf(d.getDay()) != -1){
10142                     cell.title = ddaysText;
10143                     cell.className = " x-date-disabled";
10144                 }
10145             }
10146             if(ddMatch && format){
10147                 var fvalue = d.dateFormat(format);
10148                 if(ddMatch.test(fvalue)){
10149                     cell.title = ddText.replace("%0", fvalue);
10150                     cell.className = " x-date-disabled";
10151                 }
10152             }
10153         };
10154
10155         var i = 0;
10156         for(; i < startingPos; i++) {
10157             textEls[i].innerHTML = (++prevStart);
10158             d.setDate(d.getDate()+1);
10159             cells[i].className = "x-date-prevday";
10160             setCellClass(this, cells[i]);
10161         }
10162         for(; i < days; i++){
10163             intDay = i - startingPos + 1;
10164             textEls[i].innerHTML = (intDay);
10165             d.setDate(d.getDate()+1);
10166             cells[i].className = "x-date-active";
10167             setCellClass(this, cells[i]);
10168         }
10169         var extraDays = 0;
10170         for(; i < 42; i++) {
10171              textEls[i].innerHTML = (++extraDays);
10172              d.setDate(d.getDate()+1);
10173              cells[i].className = "x-date-nextday";
10174              setCellClass(this, cells[i]);
10175         }
10176
10177         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10178         this.fireEvent('monthchange', this, date);
10179         
10180         if(!this.internalRender){
10181             var main = this.el.dom.firstChild;
10182             var w = main.offsetWidth;
10183             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10184             Roo.fly(main).setWidth(w);
10185             this.internalRender = true;
10186             // opera does not respect the auto grow header center column
10187             // then, after it gets a width opera refuses to recalculate
10188             // without a second pass
10189             if(Roo.isOpera && !this.secondPass){
10190                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10191                 this.secondPass = true;
10192                 this.update.defer(10, this, [date]);
10193             }
10194         }
10195         
10196         
10197     }
10198 });        /*
10199  * Based on:
10200  * Ext JS Library 1.1.1
10201  * Copyright(c) 2006-2007, Ext JS, LLC.
10202  *
10203  * Originally Released Under LGPL - original licence link has changed is not relivant.
10204  *
10205  * Fork - LGPL
10206  * <script type="text/javascript">
10207  */
10208 /**
10209  * @class Roo.TabPanel
10210  * @extends Roo.util.Observable
10211  * A lightweight tab container.
10212  * <br><br>
10213  * Usage:
10214  * <pre><code>
10215 // basic tabs 1, built from existing content
10216 var tabs = new Roo.TabPanel("tabs1");
10217 tabs.addTab("script", "View Script");
10218 tabs.addTab("markup", "View Markup");
10219 tabs.activate("script");
10220
10221 // more advanced tabs, built from javascript
10222 var jtabs = new Roo.TabPanel("jtabs");
10223 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10224
10225 // set up the UpdateManager
10226 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10227 var updater = tab2.getUpdateManager();
10228 updater.setDefaultUrl("ajax1.htm");
10229 tab2.on('activate', updater.refresh, updater, true);
10230
10231 // Use setUrl for Ajax loading
10232 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10233 tab3.setUrl("ajax2.htm", null, true);
10234
10235 // Disabled tab
10236 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10237 tab4.disable();
10238
10239 jtabs.activate("jtabs-1");
10240  * </code></pre>
10241  * @constructor
10242  * Create a new TabPanel.
10243  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10244  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10245  */
10246 Roo.TabPanel = function(container, config){
10247     /**
10248     * The container element for this TabPanel.
10249     * @type Roo.Element
10250     */
10251     this.el = Roo.get(container, true);
10252     if(config){
10253         if(typeof config == "boolean"){
10254             this.tabPosition = config ? "bottom" : "top";
10255         }else{
10256             Roo.apply(this, config);
10257         }
10258     }
10259     if(this.tabPosition == "bottom"){
10260         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10261         this.el.addClass("x-tabs-bottom");
10262     }
10263     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10264     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10265     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10266     if(Roo.isIE){
10267         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10268     }
10269     if(this.tabPosition != "bottom"){
10270         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10271          * @type Roo.Element
10272          */
10273         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10274         this.el.addClass("x-tabs-top");
10275     }
10276     this.items = [];
10277
10278     this.bodyEl.setStyle("position", "relative");
10279
10280     this.active = null;
10281     this.activateDelegate = this.activate.createDelegate(this);
10282
10283     this.addEvents({
10284         /**
10285          * @event tabchange
10286          * Fires when the active tab changes
10287          * @param {Roo.TabPanel} this
10288          * @param {Roo.TabPanelItem} activePanel The new active tab
10289          */
10290         "tabchange": true,
10291         /**
10292          * @event beforetabchange
10293          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10294          * @param {Roo.TabPanel} this
10295          * @param {Object} e Set cancel to true on this object to cancel the tab change
10296          * @param {Roo.TabPanelItem} tab The tab being changed to
10297          */
10298         "beforetabchange" : true
10299     });
10300
10301     Roo.EventManager.onWindowResize(this.onResize, this);
10302     this.cpad = this.el.getPadding("lr");
10303     this.hiddenCount = 0;
10304
10305
10306     // toolbar on the tabbar support...
10307     if (this.toolbar) {
10308         var tcfg = this.toolbar;
10309         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
10310         this.toolbar = new Roo.Toolbar(tcfg);
10311         if (Roo.isSafari) {
10312             var tbl = tcfg.container.child('table', true);
10313             tbl.setAttribute('width', '100%');
10314         }
10315         
10316     }
10317    
10318
10319
10320     Roo.TabPanel.superclass.constructor.call(this);
10321 };
10322
10323 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10324     /*
10325      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10326      */
10327     tabPosition : "top",
10328     /*
10329      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10330      */
10331     currentTabWidth : 0,
10332     /*
10333      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10334      */
10335     minTabWidth : 40,
10336     /*
10337      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10338      */
10339     maxTabWidth : 250,
10340     /*
10341      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10342      */
10343     preferredTabWidth : 175,
10344     /*
10345      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10346      */
10347     resizeTabs : false,
10348     /*
10349      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10350      */
10351     monitorResize : true,
10352     /*
10353      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
10354      */
10355     toolbar : false,
10356
10357     /**
10358      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10359      * @param {String} id The id of the div to use <b>or create</b>
10360      * @param {String} text The text for the tab
10361      * @param {String} content (optional) Content to put in the TabPanelItem body
10362      * @param {Boolean} closable (optional) True to create a close icon on the tab
10363      * @return {Roo.TabPanelItem} The created TabPanelItem
10364      */
10365     addTab : function(id, text, content, closable){
10366         var item = new Roo.TabPanelItem(this, id, text, closable);
10367         this.addTabItem(item);
10368         if(content){
10369             item.setContent(content);
10370         }
10371         return item;
10372     },
10373
10374     /**
10375      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10376      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10377      * @return {Roo.TabPanelItem}
10378      */
10379     getTab : function(id){
10380         return this.items[id];
10381     },
10382
10383     /**
10384      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10385      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10386      */
10387     hideTab : function(id){
10388         var t = this.items[id];
10389         if(!t.isHidden()){
10390            t.setHidden(true);
10391            this.hiddenCount++;
10392            this.autoSizeTabs();
10393         }
10394     },
10395
10396     /**
10397      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10398      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10399      */
10400     unhideTab : function(id){
10401         var t = this.items[id];
10402         if(t.isHidden()){
10403            t.setHidden(false);
10404            this.hiddenCount--;
10405            this.autoSizeTabs();
10406         }
10407     },
10408
10409     /**
10410      * Adds an existing {@link Roo.TabPanelItem}.
10411      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10412      */
10413     addTabItem : function(item){
10414         this.items[item.id] = item;
10415         this.items.push(item);
10416         if(this.resizeTabs){
10417            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10418            this.autoSizeTabs();
10419         }else{
10420             item.autoSize();
10421         }
10422     },
10423
10424     /**
10425      * Removes a {@link Roo.TabPanelItem}.
10426      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10427      */
10428     removeTab : function(id){
10429         var items = this.items;
10430         var tab = items[id];
10431         if(!tab) { return; }
10432         var index = items.indexOf(tab);
10433         if(this.active == tab && items.length > 1){
10434             var newTab = this.getNextAvailable(index);
10435             if(newTab) {
10436                 newTab.activate();
10437             }
10438         }
10439         this.stripEl.dom.removeChild(tab.pnode.dom);
10440         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10441             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10442         }
10443         items.splice(index, 1);
10444         delete this.items[tab.id];
10445         tab.fireEvent("close", tab);
10446         tab.purgeListeners();
10447         this.autoSizeTabs();
10448     },
10449
10450     getNextAvailable : function(start){
10451         var items = this.items;
10452         var index = start;
10453         // look for a next tab that will slide over to
10454         // replace the one being removed
10455         while(index < items.length){
10456             var item = items[++index];
10457             if(item && !item.isHidden()){
10458                 return item;
10459             }
10460         }
10461         // if one isn't found select the previous tab (on the left)
10462         index = start;
10463         while(index >= 0){
10464             var item = items[--index];
10465             if(item && !item.isHidden()){
10466                 return item;
10467             }
10468         }
10469         return null;
10470     },
10471
10472     /**
10473      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10474      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10475      */
10476     disableTab : function(id){
10477         var tab = this.items[id];
10478         if(tab && this.active != tab){
10479             tab.disable();
10480         }
10481     },
10482
10483     /**
10484      * Enables a {@link Roo.TabPanelItem} that is disabled.
10485      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10486      */
10487     enableTab : function(id){
10488         var tab = this.items[id];
10489         tab.enable();
10490     },
10491
10492     /**
10493      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10494      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10495      * @return {Roo.TabPanelItem} The TabPanelItem.
10496      */
10497     activate : function(id){
10498         var tab = this.items[id];
10499         if(!tab){
10500             return null;
10501         }
10502         if(tab == this.active || tab.disabled){
10503             return tab;
10504         }
10505         var e = {};
10506         this.fireEvent("beforetabchange", this, e, tab);
10507         if(e.cancel !== true && !tab.disabled){
10508             if(this.active){
10509                 this.active.hide();
10510             }
10511             this.active = this.items[id];
10512             this.active.show();
10513             this.fireEvent("tabchange", this, this.active);
10514         }
10515         return tab;
10516     },
10517
10518     /**
10519      * Gets the active {@link Roo.TabPanelItem}.
10520      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
10521      */
10522     getActiveTab : function(){
10523         return this.active;
10524     },
10525
10526     /**
10527      * Updates the tab body element to fit the height of the container element
10528      * for overflow scrolling
10529      * @param {Number} targetHeight (optional) Override the starting height from the elements height
10530      */
10531     syncHeight : function(targetHeight){
10532         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
10533         var bm = this.bodyEl.getMargins();
10534         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
10535         this.bodyEl.setHeight(newHeight);
10536         return newHeight;
10537     },
10538
10539     onResize : function(){
10540         if(this.monitorResize){
10541             this.autoSizeTabs();
10542         }
10543     },
10544
10545     /**
10546      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
10547      */
10548     beginUpdate : function(){
10549         this.updating = true;
10550     },
10551
10552     /**
10553      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
10554      */
10555     endUpdate : function(){
10556         this.updating = false;
10557         this.autoSizeTabs();
10558     },
10559
10560     /**
10561      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
10562      */
10563     autoSizeTabs : function(){
10564         var count = this.items.length;
10565         var vcount = count - this.hiddenCount;
10566         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
10567         var w = Math.max(this.el.getWidth() - this.cpad, 10);
10568         var availWidth = Math.floor(w / vcount);
10569         var b = this.stripBody;
10570         if(b.getWidth() > w){
10571             var tabs = this.items;
10572             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
10573             if(availWidth < this.minTabWidth){
10574                 /*if(!this.sleft){    // incomplete scrolling code
10575                     this.createScrollButtons();
10576                 }
10577                 this.showScroll();
10578                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
10579             }
10580         }else{
10581             if(this.currentTabWidth < this.preferredTabWidth){
10582                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
10583             }
10584         }
10585     },
10586
10587     /**
10588      * Returns the number of tabs in this TabPanel.
10589      * @return {Number}
10590      */
10591      getCount : function(){
10592          return this.items.length;
10593      },
10594
10595     /**
10596      * Resizes all the tabs to the passed width
10597      * @param {Number} The new width
10598      */
10599     setTabWidth : function(width){
10600         this.currentTabWidth = width;
10601         for(var i = 0, len = this.items.length; i < len; i++) {
10602                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
10603         }
10604     },
10605
10606     /**
10607      * Destroys this TabPanel
10608      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
10609      */
10610     destroy : function(removeEl){
10611         Roo.EventManager.removeResizeListener(this.onResize, this);
10612         for(var i = 0, len = this.items.length; i < len; i++){
10613             this.items[i].purgeListeners();
10614         }
10615         if(removeEl === true){
10616             this.el.update("");
10617             this.el.remove();
10618         }
10619     }
10620 });
10621
10622 /**
10623  * @class Roo.TabPanelItem
10624  * @extends Roo.util.Observable
10625  * Represents an individual item (tab plus body) in a TabPanel.
10626  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
10627  * @param {String} id The id of this TabPanelItem
10628  * @param {String} text The text for the tab of this TabPanelItem
10629  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
10630  */
10631 Roo.TabPanelItem = function(tabPanel, id, text, closable){
10632     /**
10633      * The {@link Roo.TabPanel} this TabPanelItem belongs to
10634      * @type Roo.TabPanel
10635      */
10636     this.tabPanel = tabPanel;
10637     /**
10638      * The id for this TabPanelItem
10639      * @type String
10640      */
10641     this.id = id;
10642     /** @private */
10643     this.disabled = false;
10644     /** @private */
10645     this.text = text;
10646     /** @private */
10647     this.loaded = false;
10648     this.closable = closable;
10649
10650     /**
10651      * The body element for this TabPanelItem.
10652      * @type Roo.Element
10653      */
10654     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
10655     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
10656     this.bodyEl.setStyle("display", "block");
10657     this.bodyEl.setStyle("zoom", "1");
10658     this.hideAction();
10659
10660     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
10661     /** @private */
10662     this.el = Roo.get(els.el, true);
10663     this.inner = Roo.get(els.inner, true);
10664     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
10665     this.pnode = Roo.get(els.el.parentNode, true);
10666     this.el.on("mousedown", this.onTabMouseDown, this);
10667     this.el.on("click", this.onTabClick, this);
10668     /** @private */
10669     if(closable){
10670         var c = Roo.get(els.close, true);
10671         c.dom.title = this.closeText;
10672         c.addClassOnOver("close-over");
10673         c.on("click", this.closeClick, this);
10674      }
10675
10676     this.addEvents({
10677          /**
10678          * @event activate
10679          * Fires when this tab becomes the active tab.
10680          * @param {Roo.TabPanel} tabPanel The parent TabPanel
10681          * @param {Roo.TabPanelItem} this
10682          */
10683         "activate": true,
10684         /**
10685          * @event beforeclose
10686          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
10687          * @param {Roo.TabPanelItem} this
10688          * @param {Object} e Set cancel to true on this object to cancel the close.
10689          */
10690         "beforeclose": true,
10691         /**
10692          * @event close
10693          * Fires when this tab is closed.
10694          * @param {Roo.TabPanelItem} this
10695          */
10696          "close": true,
10697         /**
10698          * @event deactivate
10699          * Fires when this tab is no longer the active tab.
10700          * @param {Roo.TabPanel} tabPanel The parent TabPanel
10701          * @param {Roo.TabPanelItem} this
10702          */
10703          "deactivate" : true
10704     });
10705     this.hidden = false;
10706
10707     Roo.TabPanelItem.superclass.constructor.call(this);
10708 };
10709
10710 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
10711     purgeListeners : function(){
10712        Roo.util.Observable.prototype.purgeListeners.call(this);
10713        this.el.removeAllListeners();
10714     },
10715     /**
10716      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
10717      */
10718     show : function(){
10719         this.pnode.addClass("on");
10720         this.showAction();
10721         if(Roo.isOpera){
10722             this.tabPanel.stripWrap.repaint();
10723         }
10724         this.fireEvent("activate", this.tabPanel, this);
10725     },
10726
10727     /**
10728      * Returns true if this tab is the active tab.
10729      * @return {Boolean}
10730      */
10731     isActive : function(){
10732         return this.tabPanel.getActiveTab() == this;
10733     },
10734
10735     /**
10736      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
10737      */
10738     hide : function(){
10739         this.pnode.removeClass("on");
10740         this.hideAction();
10741         this.fireEvent("deactivate", this.tabPanel, this);
10742     },
10743
10744     hideAction : function(){
10745         this.bodyEl.hide();
10746         this.bodyEl.setStyle("position", "absolute");
10747         this.bodyEl.setLeft("-20000px");
10748         this.bodyEl.setTop("-20000px");
10749     },
10750
10751     showAction : function(){
10752         this.bodyEl.setStyle("position", "relative");
10753         this.bodyEl.setTop("");
10754         this.bodyEl.setLeft("");
10755         this.bodyEl.show();
10756     },
10757
10758     /**
10759      * Set the tooltip for the tab.
10760      * @param {String} tooltip The tab's tooltip
10761      */
10762     setTooltip : function(text){
10763         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
10764             this.textEl.dom.qtip = text;
10765             this.textEl.dom.removeAttribute('title');
10766         }else{
10767             this.textEl.dom.title = text;
10768         }
10769     },
10770
10771     onTabClick : function(e){
10772         e.preventDefault();
10773         this.tabPanel.activate(this.id);
10774     },
10775
10776     onTabMouseDown : function(e){
10777         e.preventDefault();
10778         this.tabPanel.activate(this.id);
10779     },
10780
10781     getWidth : function(){
10782         return this.inner.getWidth();
10783     },
10784
10785     setWidth : function(width){
10786         var iwidth = width - this.pnode.getPadding("lr");
10787         this.inner.setWidth(iwidth);
10788         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
10789         this.pnode.setWidth(width);
10790     },
10791
10792     /**
10793      * Show or hide the tab
10794      * @param {Boolean} hidden True to hide or false to show.
10795      */
10796     setHidden : function(hidden){
10797         this.hidden = hidden;
10798         this.pnode.setStyle("display", hidden ? "none" : "");
10799     },
10800
10801     /**
10802      * Returns true if this tab is "hidden"
10803      * @return {Boolean}
10804      */
10805     isHidden : function(){
10806         return this.hidden;
10807     },
10808
10809     /**
10810      * Returns the text for this tab
10811      * @return {String}
10812      */
10813     getText : function(){
10814         return this.text;
10815     },
10816
10817     autoSize : function(){
10818         //this.el.beginMeasure();
10819         this.textEl.setWidth(1);
10820         /*
10821          *  #2804 [new] Tabs in Roojs
10822          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
10823          */
10824         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
10825         //this.el.endMeasure();
10826     },
10827
10828     /**
10829      * Sets the text for the tab (Note: this also sets the tooltip text)
10830      * @param {String} text The tab's text and tooltip
10831      */
10832     setText : function(text){
10833         this.text = text;
10834         this.textEl.update(text);
10835         this.setTooltip(text);
10836         if(!this.tabPanel.resizeTabs){
10837             this.autoSize();
10838         }
10839     },
10840     /**
10841      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
10842      */
10843     activate : function(){
10844         this.tabPanel.activate(this.id);
10845     },
10846
10847     /**
10848      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
10849      */
10850     disable : function(){
10851         if(this.tabPanel.active != this){
10852             this.disabled = true;
10853             this.pnode.addClass("disabled");
10854         }
10855     },
10856
10857     /**
10858      * Enables this TabPanelItem if it was previously disabled.
10859      */
10860     enable : function(){
10861         this.disabled = false;
10862         this.pnode.removeClass("disabled");
10863     },
10864
10865     /**
10866      * Sets the content for this TabPanelItem.
10867      * @param {String} content The content
10868      * @param {Boolean} loadScripts true to look for and load scripts
10869      */
10870     setContent : function(content, loadScripts){
10871         this.bodyEl.update(content, loadScripts);
10872     },
10873
10874     /**
10875      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
10876      * @return {Roo.UpdateManager} The UpdateManager
10877      */
10878     getUpdateManager : function(){
10879         return this.bodyEl.getUpdateManager();
10880     },
10881
10882     /**
10883      * Set a URL to be used to load the content for this TabPanelItem.
10884      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
10885      * @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)
10886      * @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)
10887      * @return {Roo.UpdateManager} The UpdateManager
10888      */
10889     setUrl : function(url, params, loadOnce){
10890         if(this.refreshDelegate){
10891             this.un('activate', this.refreshDelegate);
10892         }
10893         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
10894         this.on("activate", this.refreshDelegate);
10895         return this.bodyEl.getUpdateManager();
10896     },
10897
10898     /** @private */
10899     _handleRefresh : function(url, params, loadOnce){
10900         if(!loadOnce || !this.loaded){
10901             var updater = this.bodyEl.getUpdateManager();
10902             updater.update(url, params, this._setLoaded.createDelegate(this));
10903         }
10904     },
10905
10906     /**
10907      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
10908      *   Will fail silently if the setUrl method has not been called.
10909      *   This does not activate the panel, just updates its content.
10910      */
10911     refresh : function(){
10912         if(this.refreshDelegate){
10913            this.loaded = false;
10914            this.refreshDelegate();
10915         }
10916     },
10917
10918     /** @private */
10919     _setLoaded : function(){
10920         this.loaded = true;
10921     },
10922
10923     /** @private */
10924     closeClick : function(e){
10925         var o = {};
10926         e.stopEvent();
10927         this.fireEvent("beforeclose", this, o);
10928         if(o.cancel !== true){
10929             this.tabPanel.removeTab(this.id);
10930         }
10931     },
10932     /**
10933      * The text displayed in the tooltip for the close icon.
10934      * @type String
10935      */
10936     closeText : "Close this tab"
10937 });
10938
10939 /** @private */
10940 Roo.TabPanel.prototype.createStrip = function(container){
10941     var strip = document.createElement("div");
10942     strip.className = "x-tabs-wrap";
10943     container.appendChild(strip);
10944     return strip;
10945 };
10946 /** @private */
10947 Roo.TabPanel.prototype.createStripList = function(strip){
10948     // div wrapper for retard IE
10949     // returns the "tr" element.
10950     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
10951         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
10952         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
10953     return strip.firstChild.firstChild.firstChild.firstChild;
10954 };
10955 /** @private */
10956 Roo.TabPanel.prototype.createBody = function(container){
10957     var body = document.createElement("div");
10958     Roo.id(body, "tab-body");
10959     Roo.fly(body).addClass("x-tabs-body");
10960     container.appendChild(body);
10961     return body;
10962 };
10963 /** @private */
10964 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
10965     var body = Roo.getDom(id);
10966     if(!body){
10967         body = document.createElement("div");
10968         body.id = id;
10969     }
10970     Roo.fly(body).addClass("x-tabs-item-body");
10971     bodyEl.insertBefore(body, bodyEl.firstChild);
10972     return body;
10973 };
10974 /** @private */
10975 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
10976     var td = document.createElement("td");
10977     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
10978     //stripEl.appendChild(td);
10979     if(closable){
10980         td.className = "x-tabs-closable";
10981         if(!this.closeTpl){
10982             this.closeTpl = new Roo.Template(
10983                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
10984                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
10985                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
10986             );
10987         }
10988         var el = this.closeTpl.overwrite(td, {"text": text});
10989         var close = el.getElementsByTagName("div")[0];
10990         var inner = el.getElementsByTagName("em")[0];
10991         return {"el": el, "close": close, "inner": inner};
10992     } else {
10993         if(!this.tabTpl){
10994             this.tabTpl = new Roo.Template(
10995                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
10996                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
10997             );
10998         }
10999         var el = this.tabTpl.overwrite(td, {"text": text});
11000         var inner = el.getElementsByTagName("em")[0];
11001         return {"el": el, "inner": inner};
11002     }
11003 };/*
11004  * Based on:
11005  * Ext JS Library 1.1.1
11006  * Copyright(c) 2006-2007, Ext JS, LLC.
11007  *
11008  * Originally Released Under LGPL - original licence link has changed is not relivant.
11009  *
11010  * Fork - LGPL
11011  * <script type="text/javascript">
11012  */
11013
11014 /**
11015  * @class Roo.Button
11016  * @extends Roo.util.Observable
11017  * Simple Button class
11018  * @cfg {String} text The button text
11019  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11020  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11021  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11022  * @cfg {Object} scope The scope of the handler
11023  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11024  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11025  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11026  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11027  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11028  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11029    applies if enableToggle = true)
11030  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11031  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11032   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11033  * @constructor
11034  * Create a new button
11035  * @param {Object} config The config object
11036  */
11037 Roo.Button = function(renderTo, config)
11038 {
11039     if (!config) {
11040         config = renderTo;
11041         renderTo = config.renderTo || false;
11042     }
11043     
11044     Roo.apply(this, config);
11045     this.addEvents({
11046         /**
11047              * @event click
11048              * Fires when this button is clicked
11049              * @param {Button} this
11050              * @param {EventObject} e The click event
11051              */
11052             "click" : true,
11053         /**
11054              * @event toggle
11055              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11056              * @param {Button} this
11057              * @param {Boolean} pressed
11058              */
11059             "toggle" : true,
11060         /**
11061              * @event mouseover
11062              * Fires when the mouse hovers over the button
11063              * @param {Button} this
11064              * @param {Event} e The event object
11065              */
11066         'mouseover' : true,
11067         /**
11068              * @event mouseout
11069              * Fires when the mouse exits the button
11070              * @param {Button} this
11071              * @param {Event} e The event object
11072              */
11073         'mouseout': true,
11074          /**
11075              * @event render
11076              * Fires when the button is rendered
11077              * @param {Button} this
11078              */
11079         'render': true
11080     });
11081     if(this.menu){
11082         this.menu = Roo.menu.MenuMgr.get(this.menu);
11083     }
11084     // register listeners first!!  - so render can be captured..
11085     Roo.util.Observable.call(this);
11086     if(renderTo){
11087         this.render(renderTo);
11088     }
11089     
11090   
11091 };
11092
11093 Roo.extend(Roo.Button, Roo.util.Observable, {
11094     /**
11095      * 
11096      */
11097     
11098     /**
11099      * Read-only. True if this button is hidden
11100      * @type Boolean
11101      */
11102     hidden : false,
11103     /**
11104      * Read-only. True if this button is disabled
11105      * @type Boolean
11106      */
11107     disabled : false,
11108     /**
11109      * Read-only. True if this button is pressed (only if enableToggle = true)
11110      * @type Boolean
11111      */
11112     pressed : false,
11113
11114     /**
11115      * @cfg {Number} tabIndex 
11116      * The DOM tabIndex for this button (defaults to undefined)
11117      */
11118     tabIndex : undefined,
11119
11120     /**
11121      * @cfg {Boolean} enableToggle
11122      * True to enable pressed/not pressed toggling (defaults to false)
11123      */
11124     enableToggle: false,
11125     /**
11126      * @cfg {Mixed} menu
11127      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11128      */
11129     menu : undefined,
11130     /**
11131      * @cfg {String} menuAlign
11132      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11133      */
11134     menuAlign : "tl-bl?",
11135
11136     /**
11137      * @cfg {String} iconCls
11138      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11139      */
11140     iconCls : undefined,
11141     /**
11142      * @cfg {String} type
11143      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11144      */
11145     type : 'button',
11146
11147     // private
11148     menuClassTarget: 'tr',
11149
11150     /**
11151      * @cfg {String} clickEvent
11152      * The type of event to map to the button's event handler (defaults to 'click')
11153      */
11154     clickEvent : 'click',
11155
11156     /**
11157      * @cfg {Boolean} handleMouseEvents
11158      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11159      */
11160     handleMouseEvents : true,
11161
11162     /**
11163      * @cfg {String} tooltipType
11164      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11165      */
11166     tooltipType : 'qtip',
11167
11168     /**
11169      * @cfg {String} cls
11170      * A CSS class to apply to the button's main element.
11171      */
11172     
11173     /**
11174      * @cfg {Roo.Template} template (Optional)
11175      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11176      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11177      * require code modifications if required elements (e.g. a button) aren't present.
11178      */
11179
11180     // private
11181     render : function(renderTo){
11182         var btn;
11183         if(this.hideParent){
11184             this.parentEl = Roo.get(renderTo);
11185         }
11186         if(!this.dhconfig){
11187             if(!this.template){
11188                 if(!Roo.Button.buttonTemplate){
11189                     // hideous table template
11190                     Roo.Button.buttonTemplate = new Roo.Template(
11191                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11192                         '<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>',
11193                         "</tr></tbody></table>");
11194                 }
11195                 this.template = Roo.Button.buttonTemplate;
11196             }
11197             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11198             var btnEl = btn.child("button:first");
11199             btnEl.on('focus', this.onFocus, this);
11200             btnEl.on('blur', this.onBlur, this);
11201             if(this.cls){
11202                 btn.addClass(this.cls);
11203             }
11204             if(this.icon){
11205                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11206             }
11207             if(this.iconCls){
11208                 btnEl.addClass(this.iconCls);
11209                 if(!this.cls){
11210                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11211                 }
11212             }
11213             if(this.tabIndex !== undefined){
11214                 btnEl.dom.tabIndex = this.tabIndex;
11215             }
11216             if(this.tooltip){
11217                 if(typeof this.tooltip == 'object'){
11218                     Roo.QuickTips.tips(Roo.apply({
11219                           target: btnEl.id
11220                     }, this.tooltip));
11221                 } else {
11222                     btnEl.dom[this.tooltipType] = this.tooltip;
11223                 }
11224             }
11225         }else{
11226             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11227         }
11228         this.el = btn;
11229         if(this.id){
11230             this.el.dom.id = this.el.id = this.id;
11231         }
11232         if(this.menu){
11233             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11234             this.menu.on("show", this.onMenuShow, this);
11235             this.menu.on("hide", this.onMenuHide, this);
11236         }
11237         btn.addClass("x-btn");
11238         if(Roo.isIE && !Roo.isIE7){
11239             this.autoWidth.defer(1, this);
11240         }else{
11241             this.autoWidth();
11242         }
11243         if(this.handleMouseEvents){
11244             btn.on("mouseover", this.onMouseOver, this);
11245             btn.on("mouseout", this.onMouseOut, this);
11246             btn.on("mousedown", this.onMouseDown, this);
11247         }
11248         btn.on(this.clickEvent, this.onClick, this);
11249         //btn.on("mouseup", this.onMouseUp, this);
11250         if(this.hidden){
11251             this.hide();
11252         }
11253         if(this.disabled){
11254             this.disable();
11255         }
11256         Roo.ButtonToggleMgr.register(this);
11257         if(this.pressed){
11258             this.el.addClass("x-btn-pressed");
11259         }
11260         if(this.repeat){
11261             var repeater = new Roo.util.ClickRepeater(btn,
11262                 typeof this.repeat == "object" ? this.repeat : {}
11263             );
11264             repeater.on("click", this.onClick,  this);
11265         }
11266         
11267         this.fireEvent('render', this);
11268         
11269     },
11270     /**
11271      * Returns the button's underlying element
11272      * @return {Roo.Element} The element
11273      */
11274     getEl : function(){
11275         return this.el;  
11276     },
11277     
11278     /**
11279      * Destroys this Button and removes any listeners.
11280      */
11281     destroy : function(){
11282         Roo.ButtonToggleMgr.unregister(this);
11283         this.el.removeAllListeners();
11284         this.purgeListeners();
11285         this.el.remove();
11286     },
11287
11288     // private
11289     autoWidth : function(){
11290         if(this.el){
11291             this.el.setWidth("auto");
11292             if(Roo.isIE7 && Roo.isStrict){
11293                 var ib = this.el.child('button');
11294                 if(ib && ib.getWidth() > 20){
11295                     ib.clip();
11296                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11297                 }
11298             }
11299             if(this.minWidth){
11300                 if(this.hidden){
11301                     this.el.beginMeasure();
11302                 }
11303                 if(this.el.getWidth() < this.minWidth){
11304                     this.el.setWidth(this.minWidth);
11305                 }
11306                 if(this.hidden){
11307                     this.el.endMeasure();
11308                 }
11309             }
11310         }
11311     },
11312
11313     /**
11314      * Assigns this button's click handler
11315      * @param {Function} handler The function to call when the button is clicked
11316      * @param {Object} scope (optional) Scope for the function passed in
11317      */
11318     setHandler : function(handler, scope){
11319         this.handler = handler;
11320         this.scope = scope;  
11321     },
11322     
11323     /**
11324      * Sets this button's text
11325      * @param {String} text The button text
11326      */
11327     setText : function(text){
11328         this.text = text;
11329         if(this.el){
11330             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11331         }
11332         this.autoWidth();
11333     },
11334     
11335     /**
11336      * Gets the text for this button
11337      * @return {String} The button text
11338      */
11339     getText : function(){
11340         return this.text;  
11341     },
11342     
11343     /**
11344      * Show this button
11345      */
11346     show: function(){
11347         this.hidden = false;
11348         if(this.el){
11349             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11350         }
11351     },
11352     
11353     /**
11354      * Hide this button
11355      */
11356     hide: function(){
11357         this.hidden = true;
11358         if(this.el){
11359             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11360         }
11361     },
11362     
11363     /**
11364      * Convenience function for boolean show/hide
11365      * @param {Boolean} visible True to show, false to hide
11366      */
11367     setVisible: function(visible){
11368         if(visible) {
11369             this.show();
11370         }else{
11371             this.hide();
11372         }
11373     },
11374     
11375     /**
11376      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11377      * @param {Boolean} state (optional) Force a particular state
11378      */
11379     toggle : function(state){
11380         state = state === undefined ? !this.pressed : state;
11381         if(state != this.pressed){
11382             if(state){
11383                 this.el.addClass("x-btn-pressed");
11384                 this.pressed = true;
11385                 this.fireEvent("toggle", this, true);
11386             }else{
11387                 this.el.removeClass("x-btn-pressed");
11388                 this.pressed = false;
11389                 this.fireEvent("toggle", this, false);
11390             }
11391             if(this.toggleHandler){
11392                 this.toggleHandler.call(this.scope || this, this, state);
11393             }
11394         }
11395     },
11396     
11397     /**
11398      * Focus the button
11399      */
11400     focus : function(){
11401         this.el.child('button:first').focus();
11402     },
11403     
11404     /**
11405      * Disable this button
11406      */
11407     disable : function(){
11408         if(this.el){
11409             this.el.addClass("x-btn-disabled");
11410         }
11411         this.disabled = true;
11412     },
11413     
11414     /**
11415      * Enable this button
11416      */
11417     enable : function(){
11418         if(this.el){
11419             this.el.removeClass("x-btn-disabled");
11420         }
11421         this.disabled = false;
11422     },
11423
11424     /**
11425      * Convenience function for boolean enable/disable
11426      * @param {Boolean} enabled True to enable, false to disable
11427      */
11428     setDisabled : function(v){
11429         this[v !== true ? "enable" : "disable"]();
11430     },
11431
11432     // private
11433     onClick : function(e)
11434     {
11435         if(e){
11436             e.preventDefault();
11437         }
11438         if(e.button != 0){
11439             return;
11440         }
11441         if(!this.disabled){
11442             if(this.enableToggle){
11443                 this.toggle();
11444             }
11445             if(this.menu && !this.menu.isVisible()){
11446                 this.menu.show(this.el, this.menuAlign);
11447             }
11448             this.fireEvent("click", this, e);
11449             if(this.handler){
11450                 this.el.removeClass("x-btn-over");
11451                 this.handler.call(this.scope || this, this, e);
11452             }
11453         }
11454     },
11455     // private
11456     onMouseOver : function(e){
11457         if(!this.disabled){
11458             this.el.addClass("x-btn-over");
11459             this.fireEvent('mouseover', this, e);
11460         }
11461     },
11462     // private
11463     onMouseOut : function(e){
11464         if(!e.within(this.el,  true)){
11465             this.el.removeClass("x-btn-over");
11466             this.fireEvent('mouseout', this, e);
11467         }
11468     },
11469     // private
11470     onFocus : function(e){
11471         if(!this.disabled){
11472             this.el.addClass("x-btn-focus");
11473         }
11474     },
11475     // private
11476     onBlur : function(e){
11477         this.el.removeClass("x-btn-focus");
11478     },
11479     // private
11480     onMouseDown : function(e){
11481         if(!this.disabled && e.button == 0){
11482             this.el.addClass("x-btn-click");
11483             Roo.get(document).on('mouseup', this.onMouseUp, this);
11484         }
11485     },
11486     // private
11487     onMouseUp : function(e){
11488         if(e.button == 0){
11489             this.el.removeClass("x-btn-click");
11490             Roo.get(document).un('mouseup', this.onMouseUp, this);
11491         }
11492     },
11493     // private
11494     onMenuShow : function(e){
11495         this.el.addClass("x-btn-menu-active");
11496     },
11497     // private
11498     onMenuHide : function(e){
11499         this.el.removeClass("x-btn-menu-active");
11500     }   
11501 });
11502
11503 // Private utility class used by Button
11504 Roo.ButtonToggleMgr = function(){
11505    var groups = {};
11506    
11507    function toggleGroup(btn, state){
11508        if(state){
11509            var g = groups[btn.toggleGroup];
11510            for(var i = 0, l = g.length; i < l; i++){
11511                if(g[i] != btn){
11512                    g[i].toggle(false);
11513                }
11514            }
11515        }
11516    }
11517    
11518    return {
11519        register : function(btn){
11520            if(!btn.toggleGroup){
11521                return;
11522            }
11523            var g = groups[btn.toggleGroup];
11524            if(!g){
11525                g = groups[btn.toggleGroup] = [];
11526            }
11527            g.push(btn);
11528            btn.on("toggle", toggleGroup);
11529        },
11530        
11531        unregister : function(btn){
11532            if(!btn.toggleGroup){
11533                return;
11534            }
11535            var g = groups[btn.toggleGroup];
11536            if(g){
11537                g.remove(btn);
11538                btn.un("toggle", toggleGroup);
11539            }
11540        }
11541    };
11542 }();/*
11543  * Based on:
11544  * Ext JS Library 1.1.1
11545  * Copyright(c) 2006-2007, Ext JS, LLC.
11546  *
11547  * Originally Released Under LGPL - original licence link has changed is not relivant.
11548  *
11549  * Fork - LGPL
11550  * <script type="text/javascript">
11551  */
11552  
11553 /**
11554  * @class Roo.SplitButton
11555  * @extends Roo.Button
11556  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
11557  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
11558  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
11559  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
11560  * @cfg {String} arrowTooltip The title attribute of the arrow
11561  * @constructor
11562  * Create a new menu button
11563  * @param {String/HTMLElement/Element} renderTo The element to append the button to
11564  * @param {Object} config The config object
11565  */
11566 Roo.SplitButton = function(renderTo, config){
11567     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
11568     /**
11569      * @event arrowclick
11570      * Fires when this button's arrow is clicked
11571      * @param {SplitButton} this
11572      * @param {EventObject} e The click event
11573      */
11574     this.addEvents({"arrowclick":true});
11575 };
11576
11577 Roo.extend(Roo.SplitButton, Roo.Button, {
11578     render : function(renderTo){
11579         // this is one sweet looking template!
11580         var tpl = new Roo.Template(
11581             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
11582             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
11583             '<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>',
11584             "</tbody></table></td><td>",
11585             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
11586             '<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>',
11587             "</tbody></table></td></tr></table>"
11588         );
11589         var btn = tpl.append(renderTo, [this.text, this.type], true);
11590         var btnEl = btn.child("button");
11591         if(this.cls){
11592             btn.addClass(this.cls);
11593         }
11594         if(this.icon){
11595             btnEl.setStyle('background-image', 'url(' +this.icon +')');
11596         }
11597         if(this.iconCls){
11598             btnEl.addClass(this.iconCls);
11599             if(!this.cls){
11600                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11601             }
11602         }
11603         this.el = btn;
11604         if(this.handleMouseEvents){
11605             btn.on("mouseover", this.onMouseOver, this);
11606             btn.on("mouseout", this.onMouseOut, this);
11607             btn.on("mousedown", this.onMouseDown, this);
11608             btn.on("mouseup", this.onMouseUp, this);
11609         }
11610         btn.on(this.clickEvent, this.onClick, this);
11611         if(this.tooltip){
11612             if(typeof this.tooltip == 'object'){
11613                 Roo.QuickTips.tips(Roo.apply({
11614                       target: btnEl.id
11615                 }, this.tooltip));
11616             } else {
11617                 btnEl.dom[this.tooltipType] = this.tooltip;
11618             }
11619         }
11620         if(this.arrowTooltip){
11621             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
11622         }
11623         if(this.hidden){
11624             this.hide();
11625         }
11626         if(this.disabled){
11627             this.disable();
11628         }
11629         if(this.pressed){
11630             this.el.addClass("x-btn-pressed");
11631         }
11632         if(Roo.isIE && !Roo.isIE7){
11633             this.autoWidth.defer(1, this);
11634         }else{
11635             this.autoWidth();
11636         }
11637         if(this.menu){
11638             this.menu.on("show", this.onMenuShow, this);
11639             this.menu.on("hide", this.onMenuHide, this);
11640         }
11641         this.fireEvent('render', this);
11642     },
11643
11644     // private
11645     autoWidth : function(){
11646         if(this.el){
11647             var tbl = this.el.child("table:first");
11648             var tbl2 = this.el.child("table:last");
11649             this.el.setWidth("auto");
11650             tbl.setWidth("auto");
11651             if(Roo.isIE7 && Roo.isStrict){
11652                 var ib = this.el.child('button:first');
11653                 if(ib && ib.getWidth() > 20){
11654                     ib.clip();
11655                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11656                 }
11657             }
11658             if(this.minWidth){
11659                 if(this.hidden){
11660                     this.el.beginMeasure();
11661                 }
11662                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
11663                     tbl.setWidth(this.minWidth-tbl2.getWidth());
11664                 }
11665                 if(this.hidden){
11666                     this.el.endMeasure();
11667                 }
11668             }
11669             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
11670         } 
11671     },
11672     /**
11673      * Sets this button's click handler
11674      * @param {Function} handler The function to call when the button is clicked
11675      * @param {Object} scope (optional) Scope for the function passed above
11676      */
11677     setHandler : function(handler, scope){
11678         this.handler = handler;
11679         this.scope = scope;  
11680     },
11681     
11682     /**
11683      * Sets this button's arrow click handler
11684      * @param {Function} handler The function to call when the arrow is clicked
11685      * @param {Object} scope (optional) Scope for the function passed above
11686      */
11687     setArrowHandler : function(handler, scope){
11688         this.arrowHandler = handler;
11689         this.scope = scope;  
11690     },
11691     
11692     /**
11693      * Focus the button
11694      */
11695     focus : function(){
11696         if(this.el){
11697             this.el.child("button:first").focus();
11698         }
11699     },
11700
11701     // private
11702     onClick : function(e){
11703         e.preventDefault();
11704         if(!this.disabled){
11705             if(e.getTarget(".x-btn-menu-arrow-wrap")){
11706                 if(this.menu && !this.menu.isVisible()){
11707                     this.menu.show(this.el, this.menuAlign);
11708                 }
11709                 this.fireEvent("arrowclick", this, e);
11710                 if(this.arrowHandler){
11711                     this.arrowHandler.call(this.scope || this, this, e);
11712                 }
11713             }else{
11714                 this.fireEvent("click", this, e);
11715                 if(this.handler){
11716                     this.handler.call(this.scope || this, this, e);
11717                 }
11718             }
11719         }
11720     },
11721     // private
11722     onMouseDown : function(e){
11723         if(!this.disabled){
11724             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
11725         }
11726     },
11727     // private
11728     onMouseUp : function(e){
11729         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
11730     }   
11731 });
11732
11733
11734 // backwards compat
11735 Roo.MenuButton = Roo.SplitButton;/*
11736  * Based on:
11737  * Ext JS Library 1.1.1
11738  * Copyright(c) 2006-2007, Ext JS, LLC.
11739  *
11740  * Originally Released Under LGPL - original licence link has changed is not relivant.
11741  *
11742  * Fork - LGPL
11743  * <script type="text/javascript">
11744  */
11745
11746 /**
11747  * @class Roo.Toolbar
11748  * Basic Toolbar class.
11749  * @constructor
11750  * Creates a new Toolbar
11751  * @param {Object} container The config object
11752  */ 
11753 Roo.Toolbar = function(container, buttons, config)
11754 {
11755     /// old consturctor format still supported..
11756     if(container instanceof Array){ // omit the container for later rendering
11757         buttons = container;
11758         config = buttons;
11759         container = null;
11760     }
11761     if (typeof(container) == 'object' && container.xtype) {
11762         config = container;
11763         container = config.container;
11764         buttons = config.buttons || []; // not really - use items!!
11765     }
11766     var xitems = [];
11767     if (config && config.items) {
11768         xitems = config.items;
11769         delete config.items;
11770     }
11771     Roo.apply(this, config);
11772     this.buttons = buttons;
11773     
11774     if(container){
11775         this.render(container);
11776     }
11777     this.xitems = xitems;
11778     Roo.each(xitems, function(b) {
11779         this.add(b);
11780     }, this);
11781     
11782 };
11783
11784 Roo.Toolbar.prototype = {
11785     /**
11786      * @cfg {Array} items
11787      * array of button configs or elements to add (will be converted to a MixedCollection)
11788      */
11789     
11790     /**
11791      * @cfg {String/HTMLElement/Element} container
11792      * The id or element that will contain the toolbar
11793      */
11794     // private
11795     render : function(ct){
11796         this.el = Roo.get(ct);
11797         if(this.cls){
11798             this.el.addClass(this.cls);
11799         }
11800         // using a table allows for vertical alignment
11801         // 100% width is needed by Safari...
11802         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
11803         this.tr = this.el.child("tr", true);
11804         var autoId = 0;
11805         this.items = new Roo.util.MixedCollection(false, function(o){
11806             return o.id || ("item" + (++autoId));
11807         });
11808         if(this.buttons){
11809             this.add.apply(this, this.buttons);
11810             delete this.buttons;
11811         }
11812     },
11813
11814     /**
11815      * Adds element(s) to the toolbar -- this function takes a variable number of 
11816      * arguments of mixed type and adds them to the toolbar.
11817      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
11818      * <ul>
11819      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
11820      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
11821      * <li>Field: Any form field (equivalent to {@link #addField})</li>
11822      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
11823      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
11824      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
11825      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
11826      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
11827      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
11828      * </ul>
11829      * @param {Mixed} arg2
11830      * @param {Mixed} etc.
11831      */
11832     add : function(){
11833         var a = arguments, l = a.length;
11834         for(var i = 0; i < l; i++){
11835             this._add(a[i]);
11836         }
11837     },
11838     // private..
11839     _add : function(el) {
11840         
11841         if (el.xtype) {
11842             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
11843         }
11844         
11845         if (el.applyTo){ // some kind of form field
11846             return this.addField(el);
11847         } 
11848         if (el.render){ // some kind of Toolbar.Item
11849             return this.addItem(el);
11850         }
11851         if (typeof el == "string"){ // string
11852             if(el == "separator" || el == "-"){
11853                 return this.addSeparator();
11854             }
11855             if (el == " "){
11856                 return this.addSpacer();
11857             }
11858             if(el == "->"){
11859                 return this.addFill();
11860             }
11861             return this.addText(el);
11862             
11863         }
11864         if(el.tagName){ // element
11865             return this.addElement(el);
11866         }
11867         if(typeof el == "object"){ // must be button config?
11868             return this.addButton(el);
11869         }
11870         // and now what?!?!
11871         return false;
11872         
11873     },
11874     
11875     /**
11876      * Add an Xtype element
11877      * @param {Object} xtype Xtype Object
11878      * @return {Object} created Object
11879      */
11880     addxtype : function(e){
11881         return this.add(e);  
11882     },
11883     
11884     /**
11885      * Returns the Element for this toolbar.
11886      * @return {Roo.Element}
11887      */
11888     getEl : function(){
11889         return this.el;  
11890     },
11891     
11892     /**
11893      * Adds a separator
11894      * @return {Roo.Toolbar.Item} The separator item
11895      */
11896     addSeparator : function(){
11897         return this.addItem(new Roo.Toolbar.Separator());
11898     },
11899
11900     /**
11901      * Adds a spacer element
11902      * @return {Roo.Toolbar.Spacer} The spacer item
11903      */
11904     addSpacer : function(){
11905         return this.addItem(new Roo.Toolbar.Spacer());
11906     },
11907
11908     /**
11909      * Adds a fill element that forces subsequent additions to the right side of the toolbar
11910      * @return {Roo.Toolbar.Fill} The fill item
11911      */
11912     addFill : function(){
11913         return this.addItem(new Roo.Toolbar.Fill());
11914     },
11915
11916     /**
11917      * Adds any standard HTML element to the toolbar
11918      * @param {String/HTMLElement/Element} el The element or id of the element to add
11919      * @return {Roo.Toolbar.Item} The element's item
11920      */
11921     addElement : function(el){
11922         return this.addItem(new Roo.Toolbar.Item(el));
11923     },
11924     /**
11925      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
11926      * @type Roo.util.MixedCollection  
11927      */
11928     items : false,
11929      
11930     /**
11931      * Adds any Toolbar.Item or subclass
11932      * @param {Roo.Toolbar.Item} item
11933      * @return {Roo.Toolbar.Item} The item
11934      */
11935     addItem : function(item){
11936         var td = this.nextBlock();
11937         item.render(td);
11938         this.items.add(item);
11939         return item;
11940     },
11941     
11942     /**
11943      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
11944      * @param {Object/Array} config A button config or array of configs
11945      * @return {Roo.Toolbar.Button/Array}
11946      */
11947     addButton : function(config){
11948         if(config instanceof Array){
11949             var buttons = [];
11950             for(var i = 0, len = config.length; i < len; i++) {
11951                 buttons.push(this.addButton(config[i]));
11952             }
11953             return buttons;
11954         }
11955         var b = config;
11956         if(!(config instanceof Roo.Toolbar.Button)){
11957             b = config.split ?
11958                 new Roo.Toolbar.SplitButton(config) :
11959                 new Roo.Toolbar.Button(config);
11960         }
11961         var td = this.nextBlock();
11962         b.render(td);
11963         this.items.add(b);
11964         return b;
11965     },
11966     
11967     /**
11968      * Adds text to the toolbar
11969      * @param {String} text The text to add
11970      * @return {Roo.Toolbar.Item} The element's item
11971      */
11972     addText : function(text){
11973         return this.addItem(new Roo.Toolbar.TextItem(text));
11974     },
11975     
11976     /**
11977      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
11978      * @param {Number} index The index where the item is to be inserted
11979      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
11980      * @return {Roo.Toolbar.Button/Item}
11981      */
11982     insertButton : function(index, item){
11983         if(item instanceof Array){
11984             var buttons = [];
11985             for(var i = 0, len = item.length; i < len; i++) {
11986                buttons.push(this.insertButton(index + i, item[i]));
11987             }
11988             return buttons;
11989         }
11990         if (!(item instanceof Roo.Toolbar.Button)){
11991            item = new Roo.Toolbar.Button(item);
11992         }
11993         var td = document.createElement("td");
11994         this.tr.insertBefore(td, this.tr.childNodes[index]);
11995         item.render(td);
11996         this.items.insert(index, item);
11997         return item;
11998     },
11999     
12000     /**
12001      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12002      * @param {Object} config
12003      * @return {Roo.Toolbar.Item} The element's item
12004      */
12005     addDom : function(config, returnEl){
12006         var td = this.nextBlock();
12007         Roo.DomHelper.overwrite(td, config);
12008         var ti = new Roo.Toolbar.Item(td.firstChild);
12009         ti.render(td);
12010         this.items.add(ti);
12011         return ti;
12012     },
12013
12014     /**
12015      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12016      * @type Roo.util.MixedCollection  
12017      */
12018     fields : false,
12019     
12020     /**
12021      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12022      * Note: the field should not have been rendered yet. For a field that has already been
12023      * rendered, use {@link #addElement}.
12024      * @param {Roo.form.Field} field
12025      * @return {Roo.ToolbarItem}
12026      */
12027      
12028       
12029     addField : function(field) {
12030         if (!this.fields) {
12031             var autoId = 0;
12032             this.fields = new Roo.util.MixedCollection(false, function(o){
12033                 return o.id || ("item" + (++autoId));
12034             });
12035
12036         }
12037         
12038         var td = this.nextBlock();
12039         field.render(td);
12040         var ti = new Roo.Toolbar.Item(td.firstChild);
12041         ti.render(td);
12042         this.items.add(ti);
12043         this.fields.add(field);
12044         return ti;
12045     },
12046     /**
12047      * Hide the toolbar
12048      * @method hide
12049      */
12050      
12051       
12052     hide : function()
12053     {
12054         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12055         this.el.child('div').hide();
12056     },
12057     /**
12058      * Show the toolbar
12059      * @method show
12060      */
12061     show : function()
12062     {
12063         this.el.child('div').show();
12064     },
12065       
12066     // private
12067     nextBlock : function(){
12068         var td = document.createElement("td");
12069         this.tr.appendChild(td);
12070         return td;
12071     },
12072
12073     // private
12074     destroy : function(){
12075         if(this.items){ // rendered?
12076             Roo.destroy.apply(Roo, this.items.items);
12077         }
12078         if(this.fields){ // rendered?
12079             Roo.destroy.apply(Roo, this.fields.items);
12080         }
12081         Roo.Element.uncache(this.el, this.tr);
12082     }
12083 };
12084
12085 /**
12086  * @class Roo.Toolbar.Item
12087  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12088  * @constructor
12089  * Creates a new Item
12090  * @param {HTMLElement} el 
12091  */
12092 Roo.Toolbar.Item = function(el){
12093     var cfg = {};
12094     if (typeof (el.xtype) != 'undefined') {
12095         cfg = el;
12096         el = cfg.el;
12097     }
12098     
12099     this.el = Roo.getDom(el);
12100     this.id = Roo.id(this.el);
12101     this.hidden = false;
12102     
12103     this.addEvents({
12104          /**
12105              * @event render
12106              * Fires when the button is rendered
12107              * @param {Button} this
12108              */
12109         'render': true
12110     });
12111     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
12112 };
12113 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
12114 //Roo.Toolbar.Item.prototype = {
12115     
12116     /**
12117      * Get this item's HTML Element
12118      * @return {HTMLElement}
12119      */
12120     getEl : function(){
12121        return this.el;  
12122     },
12123
12124     // private
12125     render : function(td){
12126         
12127          this.td = td;
12128         td.appendChild(this.el);
12129         
12130         this.fireEvent('render', this);
12131     },
12132     
12133     /**
12134      * Removes and destroys this item.
12135      */
12136     destroy : function(){
12137         this.td.parentNode.removeChild(this.td);
12138     },
12139     
12140     /**
12141      * Shows this item.
12142      */
12143     show: function(){
12144         this.hidden = false;
12145         this.td.style.display = "";
12146     },
12147     
12148     /**
12149      * Hides this item.
12150      */
12151     hide: function(){
12152         this.hidden = true;
12153         this.td.style.display = "none";
12154     },
12155     
12156     /**
12157      * Convenience function for boolean show/hide.
12158      * @param {Boolean} visible true to show/false to hide
12159      */
12160     setVisible: function(visible){
12161         if(visible) {
12162             this.show();
12163         }else{
12164             this.hide();
12165         }
12166     },
12167     
12168     /**
12169      * Try to focus this item.
12170      */
12171     focus : function(){
12172         Roo.fly(this.el).focus();
12173     },
12174     
12175     /**
12176      * Disables this item.
12177      */
12178     disable : function(){
12179         Roo.fly(this.td).addClass("x-item-disabled");
12180         this.disabled = true;
12181         this.el.disabled = true;
12182     },
12183     
12184     /**
12185      * Enables this item.
12186      */
12187     enable : function(){
12188         Roo.fly(this.td).removeClass("x-item-disabled");
12189         this.disabled = false;
12190         this.el.disabled = false;
12191     }
12192 });
12193
12194
12195 /**
12196  * @class Roo.Toolbar.Separator
12197  * @extends Roo.Toolbar.Item
12198  * A simple toolbar separator class
12199  * @constructor
12200  * Creates a new Separator
12201  */
12202 Roo.Toolbar.Separator = function(cfg){
12203     
12204     var s = document.createElement("span");
12205     s.className = "ytb-sep";
12206     if (cfg) {
12207         cfg.el = s;
12208     }
12209     
12210     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
12211 };
12212 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12213     enable:Roo.emptyFn,
12214     disable:Roo.emptyFn,
12215     focus:Roo.emptyFn
12216 });
12217
12218 /**
12219  * @class Roo.Toolbar.Spacer
12220  * @extends Roo.Toolbar.Item
12221  * A simple element that adds extra horizontal space to a toolbar.
12222  * @constructor
12223  * Creates a new Spacer
12224  */
12225 Roo.Toolbar.Spacer = function(cfg){
12226     var s = document.createElement("div");
12227     s.className = "ytb-spacer";
12228     if (cfg) {
12229         cfg.el = s;
12230     }
12231     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
12232 };
12233 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12234     enable:Roo.emptyFn,
12235     disable:Roo.emptyFn,
12236     focus:Roo.emptyFn
12237 });
12238
12239 /**
12240  * @class Roo.Toolbar.Fill
12241  * @extends Roo.Toolbar.Spacer
12242  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12243  * @constructor
12244  * Creates a new Spacer
12245  */
12246 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12247     // private
12248     render : function(td){
12249         td.style.width = '100%';
12250         Roo.Toolbar.Fill.superclass.render.call(this, td);
12251     }
12252 });
12253
12254 /**
12255  * @class Roo.Toolbar.TextItem
12256  * @extends Roo.Toolbar.Item
12257  * A simple class that renders text directly into a toolbar.
12258  * @constructor
12259  * Creates a new TextItem
12260  * @param {String} text
12261  */
12262 Roo.Toolbar.TextItem = function(cfg){
12263     var  text = cfg || "";
12264     if (typeof(cfg) == 'object') {
12265         text = cfg.text || "";
12266     }  else {
12267         cfg = null;
12268     }
12269     var s = document.createElement("span");
12270     s.className = "ytb-text";
12271     s.innerHTML = text;
12272     if (cfg) {
12273         cfg.el  = s;
12274     }
12275     
12276     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
12277 };
12278 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12279     
12280      
12281     enable:Roo.emptyFn,
12282     disable:Roo.emptyFn,
12283     focus:Roo.emptyFn
12284 });
12285
12286 /**
12287  * @class Roo.Toolbar.Button
12288  * @extends Roo.Button
12289  * A button that renders into a toolbar.
12290  * @constructor
12291  * Creates a new Button
12292  * @param {Object} config A standard {@link Roo.Button} config object
12293  */
12294 Roo.Toolbar.Button = function(config){
12295     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12296 };
12297 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12298     render : function(td){
12299         this.td = td;
12300         Roo.Toolbar.Button.superclass.render.call(this, td);
12301     },
12302     
12303     /**
12304      * Removes and destroys this button
12305      */
12306     destroy : function(){
12307         Roo.Toolbar.Button.superclass.destroy.call(this);
12308         this.td.parentNode.removeChild(this.td);
12309     },
12310     
12311     /**
12312      * Shows this button
12313      */
12314     show: function(){
12315         this.hidden = false;
12316         this.td.style.display = "";
12317     },
12318     
12319     /**
12320      * Hides this button
12321      */
12322     hide: function(){
12323         this.hidden = true;
12324         this.td.style.display = "none";
12325     },
12326
12327     /**
12328      * Disables this item
12329      */
12330     disable : function(){
12331         Roo.fly(this.td).addClass("x-item-disabled");
12332         this.disabled = true;
12333     },
12334
12335     /**
12336      * Enables this item
12337      */
12338     enable : function(){
12339         Roo.fly(this.td).removeClass("x-item-disabled");
12340         this.disabled = false;
12341     }
12342 });
12343 // backwards compat
12344 Roo.ToolbarButton = Roo.Toolbar.Button;
12345
12346 /**
12347  * @class Roo.Toolbar.SplitButton
12348  * @extends Roo.SplitButton
12349  * A menu button that renders into a toolbar.
12350  * @constructor
12351  * Creates a new SplitButton
12352  * @param {Object} config A standard {@link Roo.SplitButton} config object
12353  */
12354 Roo.Toolbar.SplitButton = function(config){
12355     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12356 };
12357 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12358     render : function(td){
12359         this.td = td;
12360         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12361     },
12362     
12363     /**
12364      * Removes and destroys this button
12365      */
12366     destroy : function(){
12367         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12368         this.td.parentNode.removeChild(this.td);
12369     },
12370     
12371     /**
12372      * Shows this button
12373      */
12374     show: function(){
12375         this.hidden = false;
12376         this.td.style.display = "";
12377     },
12378     
12379     /**
12380      * Hides this button
12381      */
12382     hide: function(){
12383         this.hidden = true;
12384         this.td.style.display = "none";
12385     }
12386 });
12387
12388 // backwards compat
12389 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12390  * Based on:
12391  * Ext JS Library 1.1.1
12392  * Copyright(c) 2006-2007, Ext JS, LLC.
12393  *
12394  * Originally Released Under LGPL - original licence link has changed is not relivant.
12395  *
12396  * Fork - LGPL
12397  * <script type="text/javascript">
12398  */
12399  
12400 /**
12401  * @class Roo.PagingToolbar
12402  * @extends Roo.Toolbar
12403  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12404  * @constructor
12405  * Create a new PagingToolbar
12406  * @param {Object} config The config object
12407  */
12408 Roo.PagingToolbar = function(el, ds, config)
12409 {
12410     // old args format still supported... - xtype is prefered..
12411     if (typeof(el) == 'object' && el.xtype) {
12412         // created from xtype...
12413         config = el;
12414         ds = el.dataSource;
12415         el = config.container;
12416     }
12417     var items = [];
12418     if (config.items) {
12419         items = config.items;
12420         config.items = [];
12421     }
12422     
12423     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12424     this.ds = ds;
12425     this.cursor = 0;
12426     this.renderButtons(this.el);
12427     this.bind(ds);
12428     
12429     // supprot items array.
12430    
12431     Roo.each(items, function(e) {
12432         this.add(Roo.factory(e));
12433     },this);
12434     
12435 };
12436
12437 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12438     /**
12439      * @cfg {Roo.data.Store} dataSource
12440      * The underlying data store providing the paged data
12441      */
12442     /**
12443      * @cfg {String/HTMLElement/Element} container
12444      * container The id or element that will contain the toolbar
12445      */
12446     /**
12447      * @cfg {Boolean} displayInfo
12448      * True to display the displayMsg (defaults to false)
12449      */
12450     /**
12451      * @cfg {Number} pageSize
12452      * The number of records to display per page (defaults to 20)
12453      */
12454     pageSize: 20,
12455     /**
12456      * @cfg {String} displayMsg
12457      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12458      */
12459     displayMsg : 'Displaying {0} - {1} of {2}',
12460     /**
12461      * @cfg {String} emptyMsg
12462      * The message to display when no records are found (defaults to "No data to display")
12463      */
12464     emptyMsg : 'No data to display',
12465     /**
12466      * Customizable piece of the default paging text (defaults to "Page")
12467      * @type String
12468      */
12469     beforePageText : "Page",
12470     /**
12471      * Customizable piece of the default paging text (defaults to "of %0")
12472      * @type String
12473      */
12474     afterPageText : "of {0}",
12475     /**
12476      * Customizable piece of the default paging text (defaults to "First Page")
12477      * @type String
12478      */
12479     firstText : "First Page",
12480     /**
12481      * Customizable piece of the default paging text (defaults to "Previous Page")
12482      * @type String
12483      */
12484     prevText : "Previous Page",
12485     /**
12486      * Customizable piece of the default paging text (defaults to "Next Page")
12487      * @type String
12488      */
12489     nextText : "Next Page",
12490     /**
12491      * Customizable piece of the default paging text (defaults to "Last Page")
12492      * @type String
12493      */
12494     lastText : "Last Page",
12495     /**
12496      * Customizable piece of the default paging text (defaults to "Refresh")
12497      * @type String
12498      */
12499     refreshText : "Refresh",
12500
12501     // private
12502     renderButtons : function(el){
12503         Roo.PagingToolbar.superclass.render.call(this, el);
12504         this.first = this.addButton({
12505             tooltip: this.firstText,
12506             cls: "x-btn-icon x-grid-page-first",
12507             disabled: true,
12508             handler: this.onClick.createDelegate(this, ["first"])
12509         });
12510         this.prev = this.addButton({
12511             tooltip: this.prevText,
12512             cls: "x-btn-icon x-grid-page-prev",
12513             disabled: true,
12514             handler: this.onClick.createDelegate(this, ["prev"])
12515         });
12516         //this.addSeparator();
12517         this.add(this.beforePageText);
12518         this.field = Roo.get(this.addDom({
12519            tag: "input",
12520            type: "text",
12521            size: "3",
12522            value: "1",
12523            cls: "x-grid-page-number"
12524         }).el);
12525         this.field.on("keydown", this.onPagingKeydown, this);
12526         this.field.on("focus", function(){this.dom.select();});
12527         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12528         this.field.setHeight(18);
12529         //this.addSeparator();
12530         this.next = this.addButton({
12531             tooltip: this.nextText,
12532             cls: "x-btn-icon x-grid-page-next",
12533             disabled: true,
12534             handler: this.onClick.createDelegate(this, ["next"])
12535         });
12536         this.last = this.addButton({
12537             tooltip: this.lastText,
12538             cls: "x-btn-icon x-grid-page-last",
12539             disabled: true,
12540             handler: this.onClick.createDelegate(this, ["last"])
12541         });
12542         //this.addSeparator();
12543         this.loading = this.addButton({
12544             tooltip: this.refreshText,
12545             cls: "x-btn-icon x-grid-loading",
12546             handler: this.onClick.createDelegate(this, ["refresh"])
12547         });
12548
12549         if(this.displayInfo){
12550             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12551         }
12552     },
12553
12554     // private
12555     updateInfo : function(){
12556         if(this.displayEl){
12557             var count = this.ds.getCount();
12558             var msg = count == 0 ?
12559                 this.emptyMsg :
12560                 String.format(
12561                     this.displayMsg,
12562                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
12563                 );
12564             this.displayEl.update(msg);
12565         }
12566     },
12567
12568     // private
12569     onLoad : function(ds, r, o){
12570        this.cursor = o.params ? o.params.start : 0;
12571        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12572
12573        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
12574        this.field.dom.value = ap;
12575        this.first.setDisabled(ap == 1);
12576        this.prev.setDisabled(ap == 1);
12577        this.next.setDisabled(ap == ps);
12578        this.last.setDisabled(ap == ps);
12579        this.loading.enable();
12580        this.updateInfo();
12581     },
12582
12583     // private
12584     getPageData : function(){
12585         var total = this.ds.getTotalCount();
12586         return {
12587             total : total,
12588             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
12589             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
12590         };
12591     },
12592
12593     // private
12594     onLoadError : function(){
12595         this.loading.enable();
12596     },
12597
12598     // private
12599     onPagingKeydown : function(e){
12600         var k = e.getKey();
12601         var d = this.getPageData();
12602         if(k == e.RETURN){
12603             var v = this.field.dom.value, pageNum;
12604             if(!v || isNaN(pageNum = parseInt(v, 10))){
12605                 this.field.dom.value = d.activePage;
12606                 return;
12607             }
12608             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
12609             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12610             e.stopEvent();
12611         }
12612         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))
12613         {
12614           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
12615           this.field.dom.value = pageNum;
12616           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
12617           e.stopEvent();
12618         }
12619         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12620         {
12621           var v = this.field.dom.value, pageNum; 
12622           var increment = (e.shiftKey) ? 10 : 1;
12623           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12624             increment *= -1;
12625           if(!v || isNaN(pageNum = parseInt(v, 10))) {
12626             this.field.dom.value = d.activePage;
12627             return;
12628           }
12629           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
12630           {
12631             this.field.dom.value = parseInt(v, 10) + increment;
12632             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
12633             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12634           }
12635           e.stopEvent();
12636         }
12637     },
12638
12639     // private
12640     beforeLoad : function(){
12641         if(this.loading){
12642             this.loading.disable();
12643         }
12644     },
12645
12646     // private
12647     onClick : function(which){
12648         var ds = this.ds;
12649         switch(which){
12650             case "first":
12651                 ds.load({params:{start: 0, limit: this.pageSize}});
12652             break;
12653             case "prev":
12654                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
12655             break;
12656             case "next":
12657                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
12658             break;
12659             case "last":
12660                 var total = ds.getTotalCount();
12661                 var extra = total % this.pageSize;
12662                 var lastStart = extra ? (total - extra) : total-this.pageSize;
12663                 ds.load({params:{start: lastStart, limit: this.pageSize}});
12664             break;
12665             case "refresh":
12666                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
12667             break;
12668         }
12669     },
12670
12671     /**
12672      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
12673      * @param {Roo.data.Store} store The data store to unbind
12674      */
12675     unbind : function(ds){
12676         ds.un("beforeload", this.beforeLoad, this);
12677         ds.un("load", this.onLoad, this);
12678         ds.un("loadexception", this.onLoadError, this);
12679         ds.un("remove", this.updateInfo, this);
12680         ds.un("add", this.updateInfo, this);
12681         this.ds = undefined;
12682     },
12683
12684     /**
12685      * Binds the paging toolbar to the specified {@link Roo.data.Store}
12686      * @param {Roo.data.Store} store The data store to bind
12687      */
12688     bind : function(ds){
12689         ds.on("beforeload", this.beforeLoad, this);
12690         ds.on("load", this.onLoad, this);
12691         ds.on("loadexception", this.onLoadError, this);
12692         ds.on("remove", this.updateInfo, this);
12693         ds.on("add", this.updateInfo, this);
12694         this.ds = ds;
12695     }
12696 });/*
12697  * Based on:
12698  * Ext JS Library 1.1.1
12699  * Copyright(c) 2006-2007, Ext JS, LLC.
12700  *
12701  * Originally Released Under LGPL - original licence link has changed is not relivant.
12702  *
12703  * Fork - LGPL
12704  * <script type="text/javascript">
12705  */
12706
12707 /**
12708  * @class Roo.Resizable
12709  * @extends Roo.util.Observable
12710  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
12711  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
12712  * 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
12713  * the element will be wrapped for you automatically.</p>
12714  * <p>Here is the list of valid resize handles:</p>
12715  * <pre>
12716 Value   Description
12717 ------  -------------------
12718  'n'     north
12719  's'     south
12720  'e'     east
12721  'w'     west
12722  'nw'    northwest
12723  'sw'    southwest
12724  'se'    southeast
12725  'ne'    northeast
12726  'hd'    horizontal drag
12727  'all'   all
12728 </pre>
12729  * <p>Here's an example showing the creation of a typical Resizable:</p>
12730  * <pre><code>
12731 var resizer = new Roo.Resizable("element-id", {
12732     handles: 'all',
12733     minWidth: 200,
12734     minHeight: 100,
12735     maxWidth: 500,
12736     maxHeight: 400,
12737     pinned: true
12738 });
12739 resizer.on("resize", myHandler);
12740 </code></pre>
12741  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
12742  * resizer.east.setDisplayed(false);</p>
12743  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
12744  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
12745  * resize operation's new size (defaults to [0, 0])
12746  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
12747  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
12748  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
12749  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
12750  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
12751  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
12752  * @cfg {Number} width The width of the element in pixels (defaults to null)
12753  * @cfg {Number} height The height of the element in pixels (defaults to null)
12754  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
12755  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
12756  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
12757  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
12758  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
12759  * in favor of the handles config option (defaults to false)
12760  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
12761  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
12762  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
12763  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
12764  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
12765  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
12766  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
12767  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
12768  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
12769  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
12770  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
12771  * @constructor
12772  * Create a new resizable component
12773  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
12774  * @param {Object} config configuration options
12775   */
12776 Roo.Resizable = function(el, config)
12777 {
12778     this.el = Roo.get(el);
12779
12780     if(config && config.wrap){
12781         config.resizeChild = this.el;
12782         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
12783         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
12784         this.el.setStyle("overflow", "hidden");
12785         this.el.setPositioning(config.resizeChild.getPositioning());
12786         config.resizeChild.clearPositioning();
12787         if(!config.width || !config.height){
12788             var csize = config.resizeChild.getSize();
12789             this.el.setSize(csize.width, csize.height);
12790         }
12791         if(config.pinned && !config.adjustments){
12792             config.adjustments = "auto";
12793         }
12794     }
12795
12796     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
12797     this.proxy.unselectable();
12798     this.proxy.enableDisplayMode('block');
12799
12800     Roo.apply(this, config);
12801
12802     if(this.pinned){
12803         this.disableTrackOver = true;
12804         this.el.addClass("x-resizable-pinned");
12805     }
12806     // if the element isn't positioned, make it relative
12807     var position = this.el.getStyle("position");
12808     if(position != "absolute" && position != "fixed"){
12809         this.el.setStyle("position", "relative");
12810     }
12811     if(!this.handles){ // no handles passed, must be legacy style
12812         this.handles = 's,e,se';
12813         if(this.multiDirectional){
12814             this.handles += ',n,w';
12815         }
12816     }
12817     if(this.handles == "all"){
12818         this.handles = "n s e w ne nw se sw";
12819     }
12820     var hs = this.handles.split(/\s*?[,;]\s*?| /);
12821     var ps = Roo.Resizable.positions;
12822     for(var i = 0, len = hs.length; i < len; i++){
12823         if(hs[i] && ps[hs[i]]){
12824             var pos = ps[hs[i]];
12825             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
12826         }
12827     }
12828     // legacy
12829     this.corner = this.southeast;
12830     
12831     // updateBox = the box can move..
12832     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
12833         this.updateBox = true;
12834     }
12835
12836     this.activeHandle = null;
12837
12838     if(this.resizeChild){
12839         if(typeof this.resizeChild == "boolean"){
12840             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
12841         }else{
12842             this.resizeChild = Roo.get(this.resizeChild, true);
12843         }
12844     }
12845     
12846     if(this.adjustments == "auto"){
12847         var rc = this.resizeChild;
12848         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
12849         if(rc && (hw || hn)){
12850             rc.position("relative");
12851             rc.setLeft(hw ? hw.el.getWidth() : 0);
12852             rc.setTop(hn ? hn.el.getHeight() : 0);
12853         }
12854         this.adjustments = [
12855             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
12856             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
12857         ];
12858     }
12859
12860     if(this.draggable){
12861         this.dd = this.dynamic ?
12862             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
12863         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
12864     }
12865
12866     // public events
12867     this.addEvents({
12868         /**
12869          * @event beforeresize
12870          * Fired before resize is allowed. Set enabled to false to cancel resize.
12871          * @param {Roo.Resizable} this
12872          * @param {Roo.EventObject} e The mousedown event
12873          */
12874         "beforeresize" : true,
12875         /**
12876          * @event resizing
12877          * Fired a resizing.
12878          * @param {Roo.Resizable} this
12879          * @param {Number} x The new x position
12880          * @param {Number} y The new y position
12881          * @param {Number} w The new w width
12882          * @param {Number} h The new h hight
12883          * @param {Roo.EventObject} e The mouseup event
12884          */
12885         "resizing" : true,
12886         /**
12887          * @event resize
12888          * Fired after a resize.
12889          * @param {Roo.Resizable} this
12890          * @param {Number} width The new width
12891          * @param {Number} height The new height
12892          * @param {Roo.EventObject} e The mouseup event
12893          */
12894         "resize" : true
12895     });
12896
12897     if(this.width !== null && this.height !== null){
12898         this.resizeTo(this.width, this.height);
12899     }else{
12900         this.updateChildSize();
12901     }
12902     if(Roo.isIE){
12903         this.el.dom.style.zoom = 1;
12904     }
12905     Roo.Resizable.superclass.constructor.call(this);
12906 };
12907
12908 Roo.extend(Roo.Resizable, Roo.util.Observable, {
12909         resizeChild : false,
12910         adjustments : [0, 0],
12911         minWidth : 5,
12912         minHeight : 5,
12913         maxWidth : 10000,
12914         maxHeight : 10000,
12915         enabled : true,
12916         animate : false,
12917         duration : .35,
12918         dynamic : false,
12919         handles : false,
12920         multiDirectional : false,
12921         disableTrackOver : false,
12922         easing : 'easeOutStrong',
12923         widthIncrement : 0,
12924         heightIncrement : 0,
12925         pinned : false,
12926         width : null,
12927         height : null,
12928         preserveRatio : false,
12929         transparent: false,
12930         minX: 0,
12931         minY: 0,
12932         draggable: false,
12933
12934         /**
12935          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
12936          */
12937         constrainTo: undefined,
12938         /**
12939          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
12940          */
12941         resizeRegion: undefined,
12942
12943
12944     /**
12945      * Perform a manual resize
12946      * @param {Number} width
12947      * @param {Number} height
12948      */
12949     resizeTo : function(width, height){
12950         this.el.setSize(width, height);
12951         this.updateChildSize();
12952         this.fireEvent("resize", this, width, height, null);
12953     },
12954
12955     // private
12956     startSizing : function(e, handle){
12957         this.fireEvent("beforeresize", this, e);
12958         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
12959
12960             if(!this.overlay){
12961                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
12962                 this.overlay.unselectable();
12963                 this.overlay.enableDisplayMode("block");
12964                 this.overlay.on("mousemove", this.onMouseMove, this);
12965                 this.overlay.on("mouseup", this.onMouseUp, this);
12966             }
12967             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
12968
12969             this.resizing = true;
12970             this.startBox = this.el.getBox();
12971             this.startPoint = e.getXY();
12972             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
12973                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
12974
12975             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
12976             this.overlay.show();
12977
12978             if(this.constrainTo) {
12979                 var ct = Roo.get(this.constrainTo);
12980                 this.resizeRegion = ct.getRegion().adjust(
12981                     ct.getFrameWidth('t'),
12982                     ct.getFrameWidth('l'),
12983                     -ct.getFrameWidth('b'),
12984                     -ct.getFrameWidth('r')
12985                 );
12986             }
12987
12988             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
12989             this.proxy.show();
12990             this.proxy.setBox(this.startBox);
12991             if(!this.dynamic){
12992                 this.proxy.setStyle('visibility', 'visible');
12993             }
12994         }
12995     },
12996
12997     // private
12998     onMouseDown : function(handle, e){
12999         if(this.enabled){
13000             e.stopEvent();
13001             this.activeHandle = handle;
13002             this.startSizing(e, handle);
13003         }
13004     },
13005
13006     // private
13007     onMouseUp : function(e){
13008         var size = this.resizeElement();
13009         this.resizing = false;
13010         this.handleOut();
13011         this.overlay.hide();
13012         this.proxy.hide();
13013         this.fireEvent("resize", this, size.width, size.height, e);
13014     },
13015
13016     // private
13017     updateChildSize : function(){
13018         
13019         if(this.resizeChild){
13020             var el = this.el;
13021             var child = this.resizeChild;
13022             var adj = this.adjustments;
13023             if(el.dom.offsetWidth){
13024                 var b = el.getSize(true);
13025                 child.setSize(b.width+adj[0], b.height+adj[1]);
13026             }
13027             // Second call here for IE
13028             // The first call enables instant resizing and
13029             // the second call corrects scroll bars if they
13030             // exist
13031             if(Roo.isIE){
13032                 setTimeout(function(){
13033                     if(el.dom.offsetWidth){
13034                         var b = el.getSize(true);
13035                         child.setSize(b.width+adj[0], b.height+adj[1]);
13036                     }
13037                 }, 10);
13038             }
13039         }
13040     },
13041
13042     // private
13043     snap : function(value, inc, min){
13044         if(!inc || !value) return value;
13045         var newValue = value;
13046         var m = value % inc;
13047         if(m > 0){
13048             if(m > (inc/2)){
13049                 newValue = value + (inc-m);
13050             }else{
13051                 newValue = value - m;
13052             }
13053         }
13054         return Math.max(min, newValue);
13055     },
13056
13057     // private
13058     resizeElement : function(){
13059         var box = this.proxy.getBox();
13060         if(this.updateBox){
13061             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13062         }else{
13063             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13064         }
13065         this.updateChildSize();
13066         if(!this.dynamic){
13067             this.proxy.hide();
13068         }
13069         return box;
13070     },
13071
13072     // private
13073     constrain : function(v, diff, m, mx){
13074         if(v - diff < m){
13075             diff = v - m;
13076         }else if(v - diff > mx){
13077             diff = mx - v;
13078         }
13079         return diff;
13080     },
13081
13082     // private
13083     onMouseMove : function(e){
13084         
13085         if(this.enabled){
13086             try{// try catch so if something goes wrong the user doesn't get hung
13087
13088             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13089                 return;
13090             }
13091
13092             //var curXY = this.startPoint;
13093             var curSize = this.curSize || this.startBox;
13094             var x = this.startBox.x, y = this.startBox.y;
13095             var ox = x, oy = y;
13096             var w = curSize.width, h = curSize.height;
13097             var ow = w, oh = h;
13098             var mw = this.minWidth, mh = this.minHeight;
13099             var mxw = this.maxWidth, mxh = this.maxHeight;
13100             var wi = this.widthIncrement;
13101             var hi = this.heightIncrement;
13102
13103             var eventXY = e.getXY();
13104             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13105             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13106
13107             var pos = this.activeHandle.position;
13108
13109             switch(pos){
13110                 case "east":
13111                     w += diffX;
13112                     w = Math.min(Math.max(mw, w), mxw);
13113                     break;
13114              
13115                 case "south":
13116                     h += diffY;
13117                     h = Math.min(Math.max(mh, h), mxh);
13118                     break;
13119                 case "southeast":
13120                     w += diffX;
13121                     h += diffY;
13122                     w = Math.min(Math.max(mw, w), mxw);
13123                     h = Math.min(Math.max(mh, h), mxh);
13124                     break;
13125                 case "north":
13126                     diffY = this.constrain(h, diffY, mh, mxh);
13127                     y += diffY;
13128                     h -= diffY;
13129                     break;
13130                 case "hdrag":
13131                     
13132                     if (wi) {
13133                         var adiffX = Math.abs(diffX);
13134                         var sub = (adiffX % wi); // how much 
13135                         if (sub > (wi/2)) { // far enough to snap
13136                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13137                         } else {
13138                             // remove difference.. 
13139                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13140                         }
13141                     }
13142                     x += diffX;
13143                     x = Math.max(this.minX, x);
13144                     break;
13145                 case "west":
13146                     diffX = this.constrain(w, diffX, mw, mxw);
13147                     x += diffX;
13148                     w -= diffX;
13149                     break;
13150                 case "northeast":
13151                     w += diffX;
13152                     w = Math.min(Math.max(mw, w), mxw);
13153                     diffY = this.constrain(h, diffY, mh, mxh);
13154                     y += diffY;
13155                     h -= diffY;
13156                     break;
13157                 case "northwest":
13158                     diffX = this.constrain(w, diffX, mw, mxw);
13159                     diffY = this.constrain(h, diffY, mh, mxh);
13160                     y += diffY;
13161                     h -= diffY;
13162                     x += diffX;
13163                     w -= diffX;
13164                     break;
13165                case "southwest":
13166                     diffX = this.constrain(w, diffX, mw, mxw);
13167                     h += diffY;
13168                     h = Math.min(Math.max(mh, h), mxh);
13169                     x += diffX;
13170                     w -= diffX;
13171                     break;
13172             }
13173
13174             var sw = this.snap(w, wi, mw);
13175             var sh = this.snap(h, hi, mh);
13176             if(sw != w || sh != h){
13177                 switch(pos){
13178                     case "northeast":
13179                         y -= sh - h;
13180                     break;
13181                     case "north":
13182                         y -= sh - h;
13183                         break;
13184                     case "southwest":
13185                         x -= sw - w;
13186                     break;
13187                     case "west":
13188                         x -= sw - w;
13189                         break;
13190                     case "northwest":
13191                         x -= sw - w;
13192                         y -= sh - h;
13193                     break;
13194                 }
13195                 w = sw;
13196                 h = sh;
13197             }
13198
13199             if(this.preserveRatio){
13200                 switch(pos){
13201                     case "southeast":
13202                     case "east":
13203                         h = oh * (w/ow);
13204                         h = Math.min(Math.max(mh, h), mxh);
13205                         w = ow * (h/oh);
13206                        break;
13207                     case "south":
13208                         w = ow * (h/oh);
13209                         w = Math.min(Math.max(mw, w), mxw);
13210                         h = oh * (w/ow);
13211                         break;
13212                     case "northeast":
13213                         w = ow * (h/oh);
13214                         w = Math.min(Math.max(mw, w), mxw);
13215                         h = oh * (w/ow);
13216                     break;
13217                     case "north":
13218                         var tw = w;
13219                         w = ow * (h/oh);
13220                         w = Math.min(Math.max(mw, w), mxw);
13221                         h = oh * (w/ow);
13222                         x += (tw - w) / 2;
13223                         break;
13224                     case "southwest":
13225                         h = oh * (w/ow);
13226                         h = Math.min(Math.max(mh, h), mxh);
13227                         var tw = w;
13228                         w = ow * (h/oh);
13229                         x += tw - w;
13230                         break;
13231                     case "west":
13232                         var th = h;
13233                         h = oh * (w/ow);
13234                         h = Math.min(Math.max(mh, h), mxh);
13235                         y += (th - h) / 2;
13236                         var tw = w;
13237                         w = ow * (h/oh);
13238                         x += tw - w;
13239                        break;
13240                     case "northwest":
13241                         var tw = w;
13242                         var th = h;
13243                         h = oh * (w/ow);
13244                         h = Math.min(Math.max(mh, h), mxh);
13245                         w = ow * (h/oh);
13246                         y += th - h;
13247                         x += tw - w;
13248                        break;
13249
13250                 }
13251             }
13252             if (pos == 'hdrag') {
13253                 w = ow;
13254             }
13255             this.proxy.setBounds(x, y, w, h);
13256             if(this.dynamic){
13257                 this.resizeElement();
13258             }
13259             }catch(e){}
13260         }
13261         this.fireEvent("resizing", this, x, y, w, h, e);
13262     },
13263
13264     // private
13265     handleOver : function(){
13266         if(this.enabled){
13267             this.el.addClass("x-resizable-over");
13268         }
13269     },
13270
13271     // private
13272     handleOut : function(){
13273         if(!this.resizing){
13274             this.el.removeClass("x-resizable-over");
13275         }
13276     },
13277
13278     /**
13279      * Returns the element this component is bound to.
13280      * @return {Roo.Element}
13281      */
13282     getEl : function(){
13283         return this.el;
13284     },
13285
13286     /**
13287      * Returns the resizeChild element (or null).
13288      * @return {Roo.Element}
13289      */
13290     getResizeChild : function(){
13291         return this.resizeChild;
13292     },
13293     groupHandler : function()
13294     {
13295         
13296     },
13297     /**
13298      * Destroys this resizable. If the element was wrapped and
13299      * removeEl is not true then the element remains.
13300      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13301      */
13302     destroy : function(removeEl){
13303         this.proxy.remove();
13304         if(this.overlay){
13305             this.overlay.removeAllListeners();
13306             this.overlay.remove();
13307         }
13308         var ps = Roo.Resizable.positions;
13309         for(var k in ps){
13310             if(typeof ps[k] != "function" && this[ps[k]]){
13311                 var h = this[ps[k]];
13312                 h.el.removeAllListeners();
13313                 h.el.remove();
13314             }
13315         }
13316         if(removeEl){
13317             this.el.update("");
13318             this.el.remove();
13319         }
13320     }
13321 });
13322
13323 // private
13324 // hash to map config positions to true positions
13325 Roo.Resizable.positions = {
13326     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13327     hd: "hdrag"
13328 };
13329
13330 // private
13331 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13332     if(!this.tpl){
13333         // only initialize the template if resizable is used
13334         var tpl = Roo.DomHelper.createTemplate(
13335             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13336         );
13337         tpl.compile();
13338         Roo.Resizable.Handle.prototype.tpl = tpl;
13339     }
13340     this.position = pos;
13341     this.rz = rz;
13342     // show north drag fro topdra
13343     var handlepos = pos == 'hdrag' ? 'north' : pos;
13344     
13345     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13346     if (pos == 'hdrag') {
13347         this.el.setStyle('cursor', 'pointer');
13348     }
13349     this.el.unselectable();
13350     if(transparent){
13351         this.el.setOpacity(0);
13352     }
13353     this.el.on("mousedown", this.onMouseDown, this);
13354     if(!disableTrackOver){
13355         this.el.on("mouseover", this.onMouseOver, this);
13356         this.el.on("mouseout", this.onMouseOut, this);
13357     }
13358 };
13359
13360 // private
13361 Roo.Resizable.Handle.prototype = {
13362     afterResize : function(rz){
13363         Roo.log('after?');
13364         // do nothing
13365     },
13366     // private
13367     onMouseDown : function(e){
13368         this.rz.onMouseDown(this, e);
13369     },
13370     // private
13371     onMouseOver : function(e){
13372         this.rz.handleOver(this, e);
13373     },
13374     // private
13375     onMouseOut : function(e){
13376         this.rz.handleOut(this, e);
13377     }
13378 };/*
13379  * Based on:
13380  * Ext JS Library 1.1.1
13381  * Copyright(c) 2006-2007, Ext JS, LLC.
13382  *
13383  * Originally Released Under LGPL - original licence link has changed is not relivant.
13384  *
13385  * Fork - LGPL
13386  * <script type="text/javascript">
13387  */
13388
13389 /**
13390  * @class Roo.Editor
13391  * @extends Roo.Component
13392  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13393  * @constructor
13394  * Create a new Editor
13395  * @param {Roo.form.Field} field The Field object (or descendant)
13396  * @param {Object} config The config object
13397  */
13398 Roo.Editor = function(field, config){
13399     Roo.Editor.superclass.constructor.call(this, config);
13400     this.field = field;
13401     this.addEvents({
13402         /**
13403              * @event beforestartedit
13404              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13405              * false from the handler of this event.
13406              * @param {Editor} this
13407              * @param {Roo.Element} boundEl The underlying element bound to this editor
13408              * @param {Mixed} value The field value being set
13409              */
13410         "beforestartedit" : true,
13411         /**
13412              * @event startedit
13413              * Fires when this editor is displayed
13414              * @param {Roo.Element} boundEl The underlying element bound to this editor
13415              * @param {Mixed} value The starting field value
13416              */
13417         "startedit" : true,
13418         /**
13419              * @event beforecomplete
13420              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13421              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13422              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13423              * event will not fire since no edit actually occurred.
13424              * @param {Editor} this
13425              * @param {Mixed} value The current field value
13426              * @param {Mixed} startValue The original field value
13427              */
13428         "beforecomplete" : true,
13429         /**
13430              * @event complete
13431              * Fires after editing is complete and any changed value has been written to the underlying field.
13432              * @param {Editor} this
13433              * @param {Mixed} value The current field value
13434              * @param {Mixed} startValue The original field value
13435              */
13436         "complete" : true,
13437         /**
13438          * @event specialkey
13439          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13440          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13441          * @param {Roo.form.Field} this
13442          * @param {Roo.EventObject} e The event object
13443          */
13444         "specialkey" : true
13445     });
13446 };
13447
13448 Roo.extend(Roo.Editor, Roo.Component, {
13449     /**
13450      * @cfg {Boolean/String} autosize
13451      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13452      * or "height" to adopt the height only (defaults to false)
13453      */
13454     /**
13455      * @cfg {Boolean} revertInvalid
13456      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13457      * validation fails (defaults to true)
13458      */
13459     /**
13460      * @cfg {Boolean} ignoreNoChange
13461      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13462      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13463      * will never be ignored.
13464      */
13465     /**
13466      * @cfg {Boolean} hideEl
13467      * False to keep the bound element visible while the editor is displayed (defaults to true)
13468      */
13469     /**
13470      * @cfg {Mixed} value
13471      * The data value of the underlying field (defaults to "")
13472      */
13473     value : "",
13474     /**
13475      * @cfg {String} alignment
13476      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13477      */
13478     alignment: "c-c?",
13479     /**
13480      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13481      * for bottom-right shadow (defaults to "frame")
13482      */
13483     shadow : "frame",
13484     /**
13485      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13486      */
13487     constrain : false,
13488     /**
13489      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13490      */
13491     completeOnEnter : false,
13492     /**
13493      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13494      */
13495     cancelOnEsc : false,
13496     /**
13497      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13498      */
13499     updateEl : false,
13500
13501     // private
13502     onRender : function(ct, position){
13503         this.el = new Roo.Layer({
13504             shadow: this.shadow,
13505             cls: "x-editor",
13506             parentEl : ct,
13507             shim : this.shim,
13508             shadowOffset:4,
13509             id: this.id,
13510             constrain: this.constrain
13511         });
13512         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13513         if(this.field.msgTarget != 'title'){
13514             this.field.msgTarget = 'qtip';
13515         }
13516         this.field.render(this.el);
13517         if(Roo.isGecko){
13518             this.field.el.dom.setAttribute('autocomplete', 'off');
13519         }
13520         this.field.on("specialkey", this.onSpecialKey, this);
13521         if(this.swallowKeys){
13522             this.field.el.swallowEvent(['keydown','keypress']);
13523         }
13524         this.field.show();
13525         this.field.on("blur", this.onBlur, this);
13526         if(this.field.grow){
13527             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13528         }
13529     },
13530
13531     onSpecialKey : function(field, e)
13532     {
13533         //Roo.log('editor onSpecialKey');
13534         if(this.completeOnEnter && e.getKey() == e.ENTER){
13535             e.stopEvent();
13536             this.completeEdit();
13537             return;
13538         }
13539         // do not fire special key otherwise it might hide close the editor...
13540         if(e.getKey() == e.ENTER){    
13541             return;
13542         }
13543         if(this.cancelOnEsc && e.getKey() == e.ESC){
13544             this.cancelEdit();
13545             return;
13546         } 
13547         this.fireEvent('specialkey', field, e);
13548     
13549     },
13550
13551     /**
13552      * Starts the editing process and shows the editor.
13553      * @param {String/HTMLElement/Element} el The element to edit
13554      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13555       * to the innerHTML of el.
13556      */
13557     startEdit : function(el, value){
13558         if(this.editing){
13559             this.completeEdit();
13560         }
13561         this.boundEl = Roo.get(el);
13562         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13563         if(!this.rendered){
13564             this.render(this.parentEl || document.body);
13565         }
13566         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13567             return;
13568         }
13569         this.startValue = v;
13570         this.field.setValue(v);
13571         if(this.autoSize){
13572             var sz = this.boundEl.getSize();
13573             switch(this.autoSize){
13574                 case "width":
13575                 this.setSize(sz.width,  "");
13576                 break;
13577                 case "height":
13578                 this.setSize("",  sz.height);
13579                 break;
13580                 default:
13581                 this.setSize(sz.width,  sz.height);
13582             }
13583         }
13584         this.el.alignTo(this.boundEl, this.alignment);
13585         this.editing = true;
13586         if(Roo.QuickTips){
13587             Roo.QuickTips.disable();
13588         }
13589         this.show();
13590     },
13591
13592     /**
13593      * Sets the height and width of this editor.
13594      * @param {Number} width The new width
13595      * @param {Number} height The new height
13596      */
13597     setSize : function(w, h){
13598         this.field.setSize(w, h);
13599         if(this.el){
13600             this.el.sync();
13601         }
13602     },
13603
13604     /**
13605      * Realigns the editor to the bound field based on the current alignment config value.
13606      */
13607     realign : function(){
13608         this.el.alignTo(this.boundEl, this.alignment);
13609     },
13610
13611     /**
13612      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
13613      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
13614      */
13615     completeEdit : function(remainVisible){
13616         if(!this.editing){
13617             return;
13618         }
13619         var v = this.getValue();
13620         if(this.revertInvalid !== false && !this.field.isValid()){
13621             v = this.startValue;
13622             this.cancelEdit(true);
13623         }
13624         if(String(v) === String(this.startValue) && this.ignoreNoChange){
13625             this.editing = false;
13626             this.hide();
13627             return;
13628         }
13629         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
13630             this.editing = false;
13631             if(this.updateEl && this.boundEl){
13632                 this.boundEl.update(v);
13633             }
13634             if(remainVisible !== true){
13635                 this.hide();
13636             }
13637             this.fireEvent("complete", this, v, this.startValue);
13638         }
13639     },
13640
13641     // private
13642     onShow : function(){
13643         this.el.show();
13644         if(this.hideEl !== false){
13645             this.boundEl.hide();
13646         }
13647         this.field.show();
13648         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
13649             this.fixIEFocus = true;
13650             this.deferredFocus.defer(50, this);
13651         }else{
13652             this.field.focus();
13653         }
13654         this.fireEvent("startedit", this.boundEl, this.startValue);
13655     },
13656
13657     deferredFocus : function(){
13658         if(this.editing){
13659             this.field.focus();
13660         }
13661     },
13662
13663     /**
13664      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
13665      * reverted to the original starting value.
13666      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
13667      * cancel (defaults to false)
13668      */
13669     cancelEdit : function(remainVisible){
13670         if(this.editing){
13671             this.setValue(this.startValue);
13672             if(remainVisible !== true){
13673                 this.hide();
13674             }
13675         }
13676     },
13677
13678     // private
13679     onBlur : function(){
13680         if(this.allowBlur !== true && this.editing){
13681             this.completeEdit();
13682         }
13683     },
13684
13685     // private
13686     onHide : function(){
13687         if(this.editing){
13688             this.completeEdit();
13689             return;
13690         }
13691         this.field.blur();
13692         if(this.field.collapse){
13693             this.field.collapse();
13694         }
13695         this.el.hide();
13696         if(this.hideEl !== false){
13697             this.boundEl.show();
13698         }
13699         if(Roo.QuickTips){
13700             Roo.QuickTips.enable();
13701         }
13702     },
13703
13704     /**
13705      * Sets the data value of the editor
13706      * @param {Mixed} value Any valid value supported by the underlying field
13707      */
13708     setValue : function(v){
13709         this.field.setValue(v);
13710     },
13711
13712     /**
13713      * Gets the data value of the editor
13714      * @return {Mixed} The data value
13715      */
13716     getValue : function(){
13717         return this.field.getValue();
13718     }
13719 });/*
13720  * Based on:
13721  * Ext JS Library 1.1.1
13722  * Copyright(c) 2006-2007, Ext JS, LLC.
13723  *
13724  * Originally Released Under LGPL - original licence link has changed is not relivant.
13725  *
13726  * Fork - LGPL
13727  * <script type="text/javascript">
13728  */
13729  
13730 /**
13731  * @class Roo.BasicDialog
13732  * @extends Roo.util.Observable
13733  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
13734  * <pre><code>
13735 var dlg = new Roo.BasicDialog("my-dlg", {
13736     height: 200,
13737     width: 300,
13738     minHeight: 100,
13739     minWidth: 150,
13740     modal: true,
13741     proxyDrag: true,
13742     shadow: true
13743 });
13744 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
13745 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
13746 dlg.addButton('Cancel', dlg.hide, dlg);
13747 dlg.show();
13748 </code></pre>
13749   <b>A Dialog should always be a direct child of the body element.</b>
13750  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
13751  * @cfg {String} title Default text to display in the title bar (defaults to null)
13752  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
13753  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
13754  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
13755  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
13756  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
13757  * (defaults to null with no animation)
13758  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
13759  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
13760  * property for valid values (defaults to 'all')
13761  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
13762  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
13763  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
13764  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
13765  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
13766  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
13767  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
13768  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
13769  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
13770  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
13771  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
13772  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
13773  * draggable = true (defaults to false)
13774  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
13775  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13776  * shadow (defaults to false)
13777  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
13778  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
13779  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
13780  * @cfg {Array} buttons Array of buttons
13781  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
13782  * @constructor
13783  * Create a new BasicDialog.
13784  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
13785  * @param {Object} config Configuration options
13786  */
13787 Roo.BasicDialog = function(el, config){
13788     this.el = Roo.get(el);
13789     var dh = Roo.DomHelper;
13790     if(!this.el && config && config.autoCreate){
13791         if(typeof config.autoCreate == "object"){
13792             if(!config.autoCreate.id){
13793                 config.autoCreate.id = el;
13794             }
13795             this.el = dh.append(document.body,
13796                         config.autoCreate, true);
13797         }else{
13798             this.el = dh.append(document.body,
13799                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
13800         }
13801     }
13802     el = this.el;
13803     el.setDisplayed(true);
13804     el.hide = this.hideAction;
13805     this.id = el.id;
13806     el.addClass("x-dlg");
13807
13808     Roo.apply(this, config);
13809
13810     this.proxy = el.createProxy("x-dlg-proxy");
13811     this.proxy.hide = this.hideAction;
13812     this.proxy.setOpacity(.5);
13813     this.proxy.hide();
13814
13815     if(config.width){
13816         el.setWidth(config.width);
13817     }
13818     if(config.height){
13819         el.setHeight(config.height);
13820     }
13821     this.size = el.getSize();
13822     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
13823         this.xy = [config.x,config.y];
13824     }else{
13825         this.xy = el.getCenterXY(true);
13826     }
13827     /** The header element @type Roo.Element */
13828     this.header = el.child("> .x-dlg-hd");
13829     /** The body element @type Roo.Element */
13830     this.body = el.child("> .x-dlg-bd");
13831     /** The footer element @type Roo.Element */
13832     this.footer = el.child("> .x-dlg-ft");
13833
13834     if(!this.header){
13835         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
13836     }
13837     if(!this.body){
13838         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
13839     }
13840
13841     this.header.unselectable();
13842     if(this.title){
13843         this.header.update(this.title);
13844     }
13845     // this element allows the dialog to be focused for keyboard event
13846     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
13847     this.focusEl.swallowEvent("click", true);
13848
13849     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
13850
13851     // wrap the body and footer for special rendering
13852     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
13853     if(this.footer){
13854         this.bwrap.dom.appendChild(this.footer.dom);
13855     }
13856
13857     this.bg = this.el.createChild({
13858         tag: "div", cls:"x-dlg-bg",
13859         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
13860     });
13861     this.centerBg = this.bg.child("div.x-dlg-bg-center");
13862
13863
13864     if(this.autoScroll !== false && !this.autoTabs){
13865         this.body.setStyle("overflow", "auto");
13866     }
13867
13868     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
13869
13870     if(this.closable !== false){
13871         this.el.addClass("x-dlg-closable");
13872         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
13873         this.close.on("click", this.closeClick, this);
13874         this.close.addClassOnOver("x-dlg-close-over");
13875     }
13876     if(this.collapsible !== false){
13877         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
13878         this.collapseBtn.on("click", this.collapseClick, this);
13879         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
13880         this.header.on("dblclick", this.collapseClick, this);
13881     }
13882     if(this.resizable !== false){
13883         this.el.addClass("x-dlg-resizable");
13884         this.resizer = new Roo.Resizable(el, {
13885             minWidth: this.minWidth || 80,
13886             minHeight:this.minHeight || 80,
13887             handles: this.resizeHandles || "all",
13888             pinned: true
13889         });
13890         this.resizer.on("beforeresize", this.beforeResize, this);
13891         this.resizer.on("resize", this.onResize, this);
13892     }
13893     if(this.draggable !== false){
13894         el.addClass("x-dlg-draggable");
13895         if (!this.proxyDrag) {
13896             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
13897         }
13898         else {
13899             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
13900         }
13901         dd.setHandleElId(this.header.id);
13902         dd.endDrag = this.endMove.createDelegate(this);
13903         dd.startDrag = this.startMove.createDelegate(this);
13904         dd.onDrag = this.onDrag.createDelegate(this);
13905         dd.scroll = false;
13906         this.dd = dd;
13907     }
13908     if(this.modal){
13909         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
13910         this.mask.enableDisplayMode("block");
13911         this.mask.hide();
13912         this.el.addClass("x-dlg-modal");
13913     }
13914     if(this.shadow){
13915         this.shadow = new Roo.Shadow({
13916             mode : typeof this.shadow == "string" ? this.shadow : "sides",
13917             offset : this.shadowOffset
13918         });
13919     }else{
13920         this.shadowOffset = 0;
13921     }
13922     if(Roo.useShims && this.shim !== false){
13923         this.shim = this.el.createShim();
13924         this.shim.hide = this.hideAction;
13925         this.shim.hide();
13926     }else{
13927         this.shim = false;
13928     }
13929     if(this.autoTabs){
13930         this.initTabs();
13931     }
13932     if (this.buttons) { 
13933         var bts= this.buttons;
13934         this.buttons = [];
13935         Roo.each(bts, function(b) {
13936             this.addButton(b);
13937         }, this);
13938     }
13939     
13940     
13941     this.addEvents({
13942         /**
13943          * @event keydown
13944          * Fires when a key is pressed
13945          * @param {Roo.BasicDialog} this
13946          * @param {Roo.EventObject} e
13947          */
13948         "keydown" : true,
13949         /**
13950          * @event move
13951          * Fires when this dialog is moved by the user.
13952          * @param {Roo.BasicDialog} this
13953          * @param {Number} x The new page X
13954          * @param {Number} y The new page Y
13955          */
13956         "move" : true,
13957         /**
13958          * @event resize
13959          * Fires when this dialog is resized by the user.
13960          * @param {Roo.BasicDialog} this
13961          * @param {Number} width The new width
13962          * @param {Number} height The new height
13963          */
13964         "resize" : true,
13965         /**
13966          * @event beforehide
13967          * Fires before this dialog is hidden.
13968          * @param {Roo.BasicDialog} this
13969          */
13970         "beforehide" : true,
13971         /**
13972          * @event hide
13973          * Fires when this dialog is hidden.
13974          * @param {Roo.BasicDialog} this
13975          */
13976         "hide" : true,
13977         /**
13978          * @event beforeshow
13979          * Fires before this dialog is shown.
13980          * @param {Roo.BasicDialog} this
13981          */
13982         "beforeshow" : true,
13983         /**
13984          * @event show
13985          * Fires when this dialog is shown.
13986          * @param {Roo.BasicDialog} this
13987          */
13988         "show" : true
13989     });
13990     el.on("keydown", this.onKeyDown, this);
13991     el.on("mousedown", this.toFront, this);
13992     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
13993     this.el.hide();
13994     Roo.DialogManager.register(this);
13995     Roo.BasicDialog.superclass.constructor.call(this);
13996 };
13997
13998 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
13999     shadowOffset: Roo.isIE ? 6 : 5,
14000     minHeight: 80,
14001     minWidth: 200,
14002     minButtonWidth: 75,
14003     defaultButton: null,
14004     buttonAlign: "right",
14005     tabTag: 'div',
14006     firstShow: true,
14007
14008     /**
14009      * Sets the dialog title text
14010      * @param {String} text The title text to display
14011      * @return {Roo.BasicDialog} this
14012      */
14013     setTitle : function(text){
14014         this.header.update(text);
14015         return this;
14016     },
14017
14018     // private
14019     closeClick : function(){
14020         this.hide();
14021     },
14022
14023     // private
14024     collapseClick : function(){
14025         this[this.collapsed ? "expand" : "collapse"]();
14026     },
14027
14028     /**
14029      * Collapses the dialog to its minimized state (only the title bar is visible).
14030      * Equivalent to the user clicking the collapse dialog button.
14031      */
14032     collapse : function(){
14033         if(!this.collapsed){
14034             this.collapsed = true;
14035             this.el.addClass("x-dlg-collapsed");
14036             this.restoreHeight = this.el.getHeight();
14037             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14038         }
14039     },
14040
14041     /**
14042      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14043      * clicking the expand dialog button.
14044      */
14045     expand : function(){
14046         if(this.collapsed){
14047             this.collapsed = false;
14048             this.el.removeClass("x-dlg-collapsed");
14049             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14050         }
14051     },
14052
14053     /**
14054      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14055      * @return {Roo.TabPanel} The tabs component
14056      */
14057     initTabs : function(){
14058         var tabs = this.getTabs();
14059         while(tabs.getTab(0)){
14060             tabs.removeTab(0);
14061         }
14062         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14063             var dom = el.dom;
14064             tabs.addTab(Roo.id(dom), dom.title);
14065             dom.title = "";
14066         });
14067         tabs.activate(0);
14068         return tabs;
14069     },
14070
14071     // private
14072     beforeResize : function(){
14073         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14074     },
14075
14076     // private
14077     onResize : function(){
14078         this.refreshSize();
14079         this.syncBodyHeight();
14080         this.adjustAssets();
14081         this.focus();
14082         this.fireEvent("resize", this, this.size.width, this.size.height);
14083     },
14084
14085     // private
14086     onKeyDown : function(e){
14087         if(this.isVisible()){
14088             this.fireEvent("keydown", this, e);
14089         }
14090     },
14091
14092     /**
14093      * Resizes the dialog.
14094      * @param {Number} width
14095      * @param {Number} height
14096      * @return {Roo.BasicDialog} this
14097      */
14098     resizeTo : function(width, height){
14099         this.el.setSize(width, height);
14100         this.size = {width: width, height: height};
14101         this.syncBodyHeight();
14102         if(this.fixedcenter){
14103             this.center();
14104         }
14105         if(this.isVisible()){
14106             this.constrainXY();
14107             this.adjustAssets();
14108         }
14109         this.fireEvent("resize", this, width, height);
14110         return this;
14111     },
14112
14113
14114     /**
14115      * Resizes the dialog to fit the specified content size.
14116      * @param {Number} width
14117      * @param {Number} height
14118      * @return {Roo.BasicDialog} this
14119      */
14120     setContentSize : function(w, h){
14121         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14122         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14123         //if(!this.el.isBorderBox()){
14124             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14125             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14126         //}
14127         if(this.tabs){
14128             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14129             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14130         }
14131         this.resizeTo(w, h);
14132         return this;
14133     },
14134
14135     /**
14136      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14137      * executed in response to a particular key being pressed while the dialog is active.
14138      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14139      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14140      * @param {Function} fn The function to call
14141      * @param {Object} scope (optional) The scope of the function
14142      * @return {Roo.BasicDialog} this
14143      */
14144     addKeyListener : function(key, fn, scope){
14145         var keyCode, shift, ctrl, alt;
14146         if(typeof key == "object" && !(key instanceof Array)){
14147             keyCode = key["key"];
14148             shift = key["shift"];
14149             ctrl = key["ctrl"];
14150             alt = key["alt"];
14151         }else{
14152             keyCode = key;
14153         }
14154         var handler = function(dlg, e){
14155             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14156                 var k = e.getKey();
14157                 if(keyCode instanceof Array){
14158                     for(var i = 0, len = keyCode.length; i < len; i++){
14159                         if(keyCode[i] == k){
14160                           fn.call(scope || window, dlg, k, e);
14161                           return;
14162                         }
14163                     }
14164                 }else{
14165                     if(k == keyCode){
14166                         fn.call(scope || window, dlg, k, e);
14167                     }
14168                 }
14169             }
14170         };
14171         this.on("keydown", handler);
14172         return this;
14173     },
14174
14175     /**
14176      * Returns the TabPanel component (creates it if it doesn't exist).
14177      * Note: If you wish to simply check for the existence of tabs without creating them,
14178      * check for a null 'tabs' property.
14179      * @return {Roo.TabPanel} The tabs component
14180      */
14181     getTabs : function(){
14182         if(!this.tabs){
14183             this.el.addClass("x-dlg-auto-tabs");
14184             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14185             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14186         }
14187         return this.tabs;
14188     },
14189
14190     /**
14191      * Adds a button to the footer section of the dialog.
14192      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14193      * object or a valid Roo.DomHelper element config
14194      * @param {Function} handler The function called when the button is clicked
14195      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14196      * @return {Roo.Button} The new button
14197      */
14198     addButton : function(config, handler, scope){
14199         var dh = Roo.DomHelper;
14200         if(!this.footer){
14201             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14202         }
14203         if(!this.btnContainer){
14204             var tb = this.footer.createChild({
14205
14206                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14207                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14208             }, null, true);
14209             this.btnContainer = tb.firstChild.firstChild.firstChild;
14210         }
14211         var bconfig = {
14212             handler: handler,
14213             scope: scope,
14214             minWidth: this.minButtonWidth,
14215             hideParent:true
14216         };
14217         if(typeof config == "string"){
14218             bconfig.text = config;
14219         }else{
14220             if(config.tag){
14221                 bconfig.dhconfig = config;
14222             }else{
14223                 Roo.apply(bconfig, config);
14224             }
14225         }
14226         var fc = false;
14227         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14228             bconfig.position = Math.max(0, bconfig.position);
14229             fc = this.btnContainer.childNodes[bconfig.position];
14230         }
14231          
14232         var btn = new Roo.Button(
14233             fc ? 
14234                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14235                 : this.btnContainer.appendChild(document.createElement("td")),
14236             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14237             bconfig
14238         );
14239         this.syncBodyHeight();
14240         if(!this.buttons){
14241             /**
14242              * Array of all the buttons that have been added to this dialog via addButton
14243              * @type Array
14244              */
14245             this.buttons = [];
14246         }
14247         this.buttons.push(btn);
14248         return btn;
14249     },
14250
14251     /**
14252      * Sets the default button to be focused when the dialog is displayed.
14253      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14254      * @return {Roo.BasicDialog} this
14255      */
14256     setDefaultButton : function(btn){
14257         this.defaultButton = btn;
14258         return this;
14259     },
14260
14261     // private
14262     getHeaderFooterHeight : function(safe){
14263         var height = 0;
14264         if(this.header){
14265            height += this.header.getHeight();
14266         }
14267         if(this.footer){
14268            var fm = this.footer.getMargins();
14269             height += (this.footer.getHeight()+fm.top+fm.bottom);
14270         }
14271         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14272         height += this.centerBg.getPadding("tb");
14273         return height;
14274     },
14275
14276     // private
14277     syncBodyHeight : function()
14278     {
14279         var bd = this.body, // the text
14280             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
14281             bw = this.bwrap;
14282         var height = this.size.height - this.getHeaderFooterHeight(false);
14283         bd.setHeight(height-bd.getMargins("tb"));
14284         var hh = this.header.getHeight();
14285         var h = this.size.height-hh;
14286         cb.setHeight(h);
14287         
14288         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14289         bw.setHeight(h-cb.getPadding("tb"));
14290         
14291         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14292         bd.setWidth(bw.getWidth(true));
14293         if(this.tabs){
14294             this.tabs.syncHeight();
14295             if(Roo.isIE){
14296                 this.tabs.el.repaint();
14297             }
14298         }
14299     },
14300
14301     /**
14302      * Restores the previous state of the dialog if Roo.state is configured.
14303      * @return {Roo.BasicDialog} this
14304      */
14305     restoreState : function(){
14306         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14307         if(box && box.width){
14308             this.xy = [box.x, box.y];
14309             this.resizeTo(box.width, box.height);
14310         }
14311         return this;
14312     },
14313
14314     // private
14315     beforeShow : function(){
14316         this.expand();
14317         if(this.fixedcenter){
14318             this.xy = this.el.getCenterXY(true);
14319         }
14320         if(this.modal){
14321             Roo.get(document.body).addClass("x-body-masked");
14322             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14323             this.mask.show();
14324         }
14325         this.constrainXY();
14326     },
14327
14328     // private
14329     animShow : function(){
14330         var b = Roo.get(this.animateTarget).getBox();
14331         this.proxy.setSize(b.width, b.height);
14332         this.proxy.setLocation(b.x, b.y);
14333         this.proxy.show();
14334         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14335                     true, .35, this.showEl.createDelegate(this));
14336     },
14337
14338     /**
14339      * Shows the dialog.
14340      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14341      * @return {Roo.BasicDialog} this
14342      */
14343     show : function(animateTarget){
14344         if (this.fireEvent("beforeshow", this) === false){
14345             return;
14346         }
14347         if(this.syncHeightBeforeShow){
14348             this.syncBodyHeight();
14349         }else if(this.firstShow){
14350             this.firstShow = false;
14351             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14352         }
14353         this.animateTarget = animateTarget || this.animateTarget;
14354         if(!this.el.isVisible()){
14355             this.beforeShow();
14356             if(this.animateTarget && Roo.get(this.animateTarget)){
14357                 this.animShow();
14358             }else{
14359                 this.showEl();
14360             }
14361         }
14362         return this;
14363     },
14364
14365     // private
14366     showEl : function(){
14367         this.proxy.hide();
14368         this.el.setXY(this.xy);
14369         this.el.show();
14370         this.adjustAssets(true);
14371         this.toFront();
14372         this.focus();
14373         // IE peekaboo bug - fix found by Dave Fenwick
14374         if(Roo.isIE){
14375             this.el.repaint();
14376         }
14377         this.fireEvent("show", this);
14378     },
14379
14380     /**
14381      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14382      * dialog itself will receive focus.
14383      */
14384     focus : function(){
14385         if(this.defaultButton){
14386             this.defaultButton.focus();
14387         }else{
14388             this.focusEl.focus();
14389         }
14390     },
14391
14392     // private
14393     constrainXY : function(){
14394         if(this.constraintoviewport !== false){
14395             if(!this.viewSize){
14396                 if(this.container){
14397                     var s = this.container.getSize();
14398                     this.viewSize = [s.width, s.height];
14399                 }else{
14400                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14401                 }
14402             }
14403             var s = Roo.get(this.container||document).getScroll();
14404
14405             var x = this.xy[0], y = this.xy[1];
14406             var w = this.size.width, h = this.size.height;
14407             var vw = this.viewSize[0], vh = this.viewSize[1];
14408             // only move it if it needs it
14409             var moved = false;
14410             // first validate right/bottom
14411             if(x + w > vw+s.left){
14412                 x = vw - w;
14413                 moved = true;
14414             }
14415             if(y + h > vh+s.top){
14416                 y = vh - h;
14417                 moved = true;
14418             }
14419             // then make sure top/left isn't negative
14420             if(x < s.left){
14421                 x = s.left;
14422                 moved = true;
14423             }
14424             if(y < s.top){
14425                 y = s.top;
14426                 moved = true;
14427             }
14428             if(moved){
14429                 // cache xy
14430                 this.xy = [x, y];
14431                 if(this.isVisible()){
14432                     this.el.setLocation(x, y);
14433                     this.adjustAssets();
14434                 }
14435             }
14436         }
14437     },
14438
14439     // private
14440     onDrag : function(){
14441         if(!this.proxyDrag){
14442             this.xy = this.el.getXY();
14443             this.adjustAssets();
14444         }
14445     },
14446
14447     // private
14448     adjustAssets : function(doShow){
14449         var x = this.xy[0], y = this.xy[1];
14450         var w = this.size.width, h = this.size.height;
14451         if(doShow === true){
14452             if(this.shadow){
14453                 this.shadow.show(this.el);
14454             }
14455             if(this.shim){
14456                 this.shim.show();
14457             }
14458         }
14459         if(this.shadow && this.shadow.isVisible()){
14460             this.shadow.show(this.el);
14461         }
14462         if(this.shim && this.shim.isVisible()){
14463             this.shim.setBounds(x, y, w, h);
14464         }
14465     },
14466
14467     // private
14468     adjustViewport : function(w, h){
14469         if(!w || !h){
14470             w = Roo.lib.Dom.getViewWidth();
14471             h = Roo.lib.Dom.getViewHeight();
14472         }
14473         // cache the size
14474         this.viewSize = [w, h];
14475         if(this.modal && this.mask.isVisible()){
14476             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14477             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14478         }
14479         if(this.isVisible()){
14480             this.constrainXY();
14481         }
14482     },
14483
14484     /**
14485      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14486      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14487      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14488      */
14489     destroy : function(removeEl){
14490         if(this.isVisible()){
14491             this.animateTarget = null;
14492             this.hide();
14493         }
14494         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14495         if(this.tabs){
14496             this.tabs.destroy(removeEl);
14497         }
14498         Roo.destroy(
14499              this.shim,
14500              this.proxy,
14501              this.resizer,
14502              this.close,
14503              this.mask
14504         );
14505         if(this.dd){
14506             this.dd.unreg();
14507         }
14508         if(this.buttons){
14509            for(var i = 0, len = this.buttons.length; i < len; i++){
14510                this.buttons[i].destroy();
14511            }
14512         }
14513         this.el.removeAllListeners();
14514         if(removeEl === true){
14515             this.el.update("");
14516             this.el.remove();
14517         }
14518         Roo.DialogManager.unregister(this);
14519     },
14520
14521     // private
14522     startMove : function(){
14523         if(this.proxyDrag){
14524             this.proxy.show();
14525         }
14526         if(this.constraintoviewport !== false){
14527             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14528         }
14529     },
14530
14531     // private
14532     endMove : function(){
14533         if(!this.proxyDrag){
14534             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14535         }else{
14536             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14537             this.proxy.hide();
14538         }
14539         this.refreshSize();
14540         this.adjustAssets();
14541         this.focus();
14542         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14543     },
14544
14545     /**
14546      * Brings this dialog to the front of any other visible dialogs
14547      * @return {Roo.BasicDialog} this
14548      */
14549     toFront : function(){
14550         Roo.DialogManager.bringToFront(this);
14551         return this;
14552     },
14553
14554     /**
14555      * Sends this dialog to the back (under) of any other visible dialogs
14556      * @return {Roo.BasicDialog} this
14557      */
14558     toBack : function(){
14559         Roo.DialogManager.sendToBack(this);
14560         return this;
14561     },
14562
14563     /**
14564      * Centers this dialog in the viewport
14565      * @return {Roo.BasicDialog} this
14566      */
14567     center : function(){
14568         var xy = this.el.getCenterXY(true);
14569         this.moveTo(xy[0], xy[1]);
14570         return this;
14571     },
14572
14573     /**
14574      * Moves the dialog's top-left corner to the specified point
14575      * @param {Number} x
14576      * @param {Number} y
14577      * @return {Roo.BasicDialog} this
14578      */
14579     moveTo : function(x, y){
14580         this.xy = [x,y];
14581         if(this.isVisible()){
14582             this.el.setXY(this.xy);
14583             this.adjustAssets();
14584         }
14585         return this;
14586     },
14587
14588     /**
14589      * Aligns the dialog to the specified element
14590      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14591      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
14592      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14593      * @return {Roo.BasicDialog} this
14594      */
14595     alignTo : function(element, position, offsets){
14596         this.xy = this.el.getAlignToXY(element, position, offsets);
14597         if(this.isVisible()){
14598             this.el.setXY(this.xy);
14599             this.adjustAssets();
14600         }
14601         return this;
14602     },
14603
14604     /**
14605      * Anchors an element to another element and realigns it when the window is resized.
14606      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14607      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
14608      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14609      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
14610      * is a number, it is used as the buffer delay (defaults to 50ms).
14611      * @return {Roo.BasicDialog} this
14612      */
14613     anchorTo : function(el, alignment, offsets, monitorScroll){
14614         var action = function(){
14615             this.alignTo(el, alignment, offsets);
14616         };
14617         Roo.EventManager.onWindowResize(action, this);
14618         var tm = typeof monitorScroll;
14619         if(tm != 'undefined'){
14620             Roo.EventManager.on(window, 'scroll', action, this,
14621                 {buffer: tm == 'number' ? monitorScroll : 50});
14622         }
14623         action.call(this);
14624         return this;
14625     },
14626
14627     /**
14628      * Returns true if the dialog is visible
14629      * @return {Boolean}
14630      */
14631     isVisible : function(){
14632         return this.el.isVisible();
14633     },
14634
14635     // private
14636     animHide : function(callback){
14637         var b = Roo.get(this.animateTarget).getBox();
14638         this.proxy.show();
14639         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
14640         this.el.hide();
14641         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
14642                     this.hideEl.createDelegate(this, [callback]));
14643     },
14644
14645     /**
14646      * Hides the dialog.
14647      * @param {Function} callback (optional) Function to call when the dialog is hidden
14648      * @return {Roo.BasicDialog} this
14649      */
14650     hide : function(callback){
14651         if (this.fireEvent("beforehide", this) === false){
14652             return;
14653         }
14654         if(this.shadow){
14655             this.shadow.hide();
14656         }
14657         if(this.shim) {
14658           this.shim.hide();
14659         }
14660         // sometimes animateTarget seems to get set.. causing problems...
14661         // this just double checks..
14662         if(this.animateTarget && Roo.get(this.animateTarget)) {
14663            this.animHide(callback);
14664         }else{
14665             this.el.hide();
14666             this.hideEl(callback);
14667         }
14668         return this;
14669     },
14670
14671     // private
14672     hideEl : function(callback){
14673         this.proxy.hide();
14674         if(this.modal){
14675             this.mask.hide();
14676             Roo.get(document.body).removeClass("x-body-masked");
14677         }
14678         this.fireEvent("hide", this);
14679         if(typeof callback == "function"){
14680             callback();
14681         }
14682     },
14683
14684     // private
14685     hideAction : function(){
14686         this.setLeft("-10000px");
14687         this.setTop("-10000px");
14688         this.setStyle("visibility", "hidden");
14689     },
14690
14691     // private
14692     refreshSize : function(){
14693         this.size = this.el.getSize();
14694         this.xy = this.el.getXY();
14695         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
14696     },
14697
14698     // private
14699     // z-index is managed by the DialogManager and may be overwritten at any time
14700     setZIndex : function(index){
14701         if(this.modal){
14702             this.mask.setStyle("z-index", index);
14703         }
14704         if(this.shim){
14705             this.shim.setStyle("z-index", ++index);
14706         }
14707         if(this.shadow){
14708             this.shadow.setZIndex(++index);
14709         }
14710         this.el.setStyle("z-index", ++index);
14711         if(this.proxy){
14712             this.proxy.setStyle("z-index", ++index);
14713         }
14714         if(this.resizer){
14715             this.resizer.proxy.setStyle("z-index", ++index);
14716         }
14717
14718         this.lastZIndex = index;
14719     },
14720
14721     /**
14722      * Returns the element for this dialog
14723      * @return {Roo.Element} The underlying dialog Element
14724      */
14725     getEl : function(){
14726         return this.el;
14727     }
14728 });
14729
14730 /**
14731  * @class Roo.DialogManager
14732  * Provides global access to BasicDialogs that have been created and
14733  * support for z-indexing (layering) multiple open dialogs.
14734  */
14735 Roo.DialogManager = function(){
14736     var list = {};
14737     var accessList = [];
14738     var front = null;
14739
14740     // private
14741     var sortDialogs = function(d1, d2){
14742         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
14743     };
14744
14745     // private
14746     var orderDialogs = function(){
14747         accessList.sort(sortDialogs);
14748         var seed = Roo.DialogManager.zseed;
14749         for(var i = 0, len = accessList.length; i < len; i++){
14750             var dlg = accessList[i];
14751             if(dlg){
14752                 dlg.setZIndex(seed + (i*10));
14753             }
14754         }
14755     };
14756
14757     return {
14758         /**
14759          * The starting z-index for BasicDialogs (defaults to 9000)
14760          * @type Number The z-index value
14761          */
14762         zseed : 9000,
14763
14764         // private
14765         register : function(dlg){
14766             list[dlg.id] = dlg;
14767             accessList.push(dlg);
14768         },
14769
14770         // private
14771         unregister : function(dlg){
14772             delete list[dlg.id];
14773             var i=0;
14774             var len=0;
14775             if(!accessList.indexOf){
14776                 for(  i = 0, len = accessList.length; i < len; i++){
14777                     if(accessList[i] == dlg){
14778                         accessList.splice(i, 1);
14779                         return;
14780                     }
14781                 }
14782             }else{
14783                  i = accessList.indexOf(dlg);
14784                 if(i != -1){
14785                     accessList.splice(i, 1);
14786                 }
14787             }
14788         },
14789
14790         /**
14791          * Gets a registered dialog by id
14792          * @param {String/Object} id The id of the dialog or a dialog
14793          * @return {Roo.BasicDialog} this
14794          */
14795         get : function(id){
14796             return typeof id == "object" ? id : list[id];
14797         },
14798
14799         /**
14800          * Brings the specified dialog to the front
14801          * @param {String/Object} dlg The id of the dialog or a dialog
14802          * @return {Roo.BasicDialog} this
14803          */
14804         bringToFront : function(dlg){
14805             dlg = this.get(dlg);
14806             if(dlg != front){
14807                 front = dlg;
14808                 dlg._lastAccess = new Date().getTime();
14809                 orderDialogs();
14810             }
14811             return dlg;
14812         },
14813
14814         /**
14815          * Sends the specified dialog to the back
14816          * @param {String/Object} dlg The id of the dialog or a dialog
14817          * @return {Roo.BasicDialog} this
14818          */
14819         sendToBack : function(dlg){
14820             dlg = this.get(dlg);
14821             dlg._lastAccess = -(new Date().getTime());
14822             orderDialogs();
14823             return dlg;
14824         },
14825
14826         /**
14827          * Hides all dialogs
14828          */
14829         hideAll : function(){
14830             for(var id in list){
14831                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
14832                     list[id].hide();
14833                 }
14834             }
14835         }
14836     };
14837 }();
14838
14839 /**
14840  * @class Roo.LayoutDialog
14841  * @extends Roo.BasicDialog
14842  * Dialog which provides adjustments for working with a layout in a Dialog.
14843  * Add your necessary layout config options to the dialog's config.<br>
14844  * Example usage (including a nested layout):
14845  * <pre><code>
14846 if(!dialog){
14847     dialog = new Roo.LayoutDialog("download-dlg", {
14848         modal: true,
14849         width:600,
14850         height:450,
14851         shadow:true,
14852         minWidth:500,
14853         minHeight:350,
14854         autoTabs:true,
14855         proxyDrag:true,
14856         // layout config merges with the dialog config
14857         center:{
14858             tabPosition: "top",
14859             alwaysShowTabs: true
14860         }
14861     });
14862     dialog.addKeyListener(27, dialog.hide, dialog);
14863     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
14864     dialog.addButton("Build It!", this.getDownload, this);
14865
14866     // we can even add nested layouts
14867     var innerLayout = new Roo.BorderLayout("dl-inner", {
14868         east: {
14869             initialSize: 200,
14870             autoScroll:true,
14871             split:true
14872         },
14873         center: {
14874             autoScroll:true
14875         }
14876     });
14877     innerLayout.beginUpdate();
14878     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
14879     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
14880     innerLayout.endUpdate(true);
14881
14882     var layout = dialog.getLayout();
14883     layout.beginUpdate();
14884     layout.add("center", new Roo.ContentPanel("standard-panel",
14885                         {title: "Download the Source", fitToFrame:true}));
14886     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
14887                {title: "Build your own roo.js"}));
14888     layout.getRegion("center").showPanel(sp);
14889     layout.endUpdate();
14890 }
14891 </code></pre>
14892     * @constructor
14893     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
14894     * @param {Object} config configuration options
14895   */
14896 Roo.LayoutDialog = function(el, cfg){
14897     
14898     var config=  cfg;
14899     if (typeof(cfg) == 'undefined') {
14900         config = Roo.apply({}, el);
14901         // not sure why we use documentElement here.. - it should always be body.
14902         // IE7 borks horribly if we use documentElement.
14903         // webkit also does not like documentElement - it creates a body element...
14904         el = Roo.get( document.body || document.documentElement ).createChild();
14905         //config.autoCreate = true;
14906     }
14907     
14908     
14909     config.autoTabs = false;
14910     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
14911     this.body.setStyle({overflow:"hidden", position:"relative"});
14912     this.layout = new Roo.BorderLayout(this.body.dom, config);
14913     this.layout.monitorWindowResize = false;
14914     this.el.addClass("x-dlg-auto-layout");
14915     // fix case when center region overwrites center function
14916     this.center = Roo.BasicDialog.prototype.center;
14917     this.on("show", this.layout.layout, this.layout, true);
14918     if (config.items) {
14919         var xitems = config.items;
14920         delete config.items;
14921         Roo.each(xitems, this.addxtype, this);
14922     }
14923     
14924     
14925 };
14926 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
14927     /**
14928      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
14929      * @deprecated
14930      */
14931     endUpdate : function(){
14932         this.layout.endUpdate();
14933     },
14934
14935     /**
14936      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
14937      *  @deprecated
14938      */
14939     beginUpdate : function(){
14940         this.layout.beginUpdate();
14941     },
14942
14943     /**
14944      * Get the BorderLayout for this dialog
14945      * @return {Roo.BorderLayout}
14946      */
14947     getLayout : function(){
14948         return this.layout;
14949     },
14950
14951     showEl : function(){
14952         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
14953         if(Roo.isIE7){
14954             this.layout.layout();
14955         }
14956     },
14957
14958     // private
14959     // Use the syncHeightBeforeShow config option to control this automatically
14960     syncBodyHeight : function(){
14961         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
14962         if(this.layout){this.layout.layout();}
14963     },
14964     
14965       /**
14966      * Add an xtype element (actually adds to the layout.)
14967      * @return {Object} xdata xtype object data.
14968      */
14969     
14970     addxtype : function(c) {
14971         return this.layout.addxtype(c);
14972     }
14973 });/*
14974  * Based on:
14975  * Ext JS Library 1.1.1
14976  * Copyright(c) 2006-2007, Ext JS, LLC.
14977  *
14978  * Originally Released Under LGPL - original licence link has changed is not relivant.
14979  *
14980  * Fork - LGPL
14981  * <script type="text/javascript">
14982  */
14983  
14984 /**
14985  * @class Roo.MessageBox
14986  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
14987  * Example usage:
14988  *<pre><code>
14989 // Basic alert:
14990 Roo.Msg.alert('Status', 'Changes saved successfully.');
14991
14992 // Prompt for user data:
14993 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
14994     if (btn == 'ok'){
14995         // process text value...
14996     }
14997 });
14998
14999 // Show a dialog using config options:
15000 Roo.Msg.show({
15001    title:'Save Changes?',
15002    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15003    buttons: Roo.Msg.YESNOCANCEL,
15004    fn: processResult,
15005    animEl: 'elId'
15006 });
15007 </code></pre>
15008  * @singleton
15009  */
15010 Roo.MessageBox = function(){
15011     var dlg, opt, mask, waitTimer;
15012     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15013     var buttons, activeTextEl, bwidth;
15014
15015     // private
15016     var handleButton = function(button){
15017         dlg.hide();
15018         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15019     };
15020
15021     // private
15022     var handleHide = function(){
15023         if(opt && opt.cls){
15024             dlg.el.removeClass(opt.cls);
15025         }
15026         if(waitTimer){
15027             Roo.TaskMgr.stop(waitTimer);
15028             waitTimer = null;
15029         }
15030     };
15031
15032     // private
15033     var updateButtons = function(b){
15034         var width = 0;
15035         if(!b){
15036             buttons["ok"].hide();
15037             buttons["cancel"].hide();
15038             buttons["yes"].hide();
15039             buttons["no"].hide();
15040             dlg.footer.dom.style.display = 'none';
15041             return width;
15042         }
15043         dlg.footer.dom.style.display = '';
15044         for(var k in buttons){
15045             if(typeof buttons[k] != "function"){
15046                 if(b[k]){
15047                     buttons[k].show();
15048                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15049                     width += buttons[k].el.getWidth()+15;
15050                 }else{
15051                     buttons[k].hide();
15052                 }
15053             }
15054         }
15055         return width;
15056     };
15057
15058     // private
15059     var handleEsc = function(d, k, e){
15060         if(opt && opt.closable !== false){
15061             dlg.hide();
15062         }
15063         if(e){
15064             e.stopEvent();
15065         }
15066     };
15067
15068     return {
15069         /**
15070          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15071          * @return {Roo.BasicDialog} The BasicDialog element
15072          */
15073         getDialog : function(){
15074            if(!dlg){
15075                 dlg = new Roo.BasicDialog("x-msg-box", {
15076                     autoCreate : true,
15077                     shadow: true,
15078                     draggable: true,
15079                     resizable:false,
15080                     constraintoviewport:false,
15081                     fixedcenter:true,
15082                     collapsible : false,
15083                     shim:true,
15084                     modal: true,
15085                     width:400, height:100,
15086                     buttonAlign:"center",
15087                     closeClick : function(){
15088                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15089                             handleButton("no");
15090                         }else{
15091                             handleButton("cancel");
15092                         }
15093                     }
15094                 });
15095                 dlg.on("hide", handleHide);
15096                 mask = dlg.mask;
15097                 dlg.addKeyListener(27, handleEsc);
15098                 buttons = {};
15099                 var bt = this.buttonText;
15100                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15101                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15102                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15103                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15104                 bodyEl = dlg.body.createChild({
15105
15106                     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>'
15107                 });
15108                 msgEl = bodyEl.dom.firstChild;
15109                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15110                 textboxEl.enableDisplayMode();
15111                 textboxEl.addKeyListener([10,13], function(){
15112                     if(dlg.isVisible() && opt && opt.buttons){
15113                         if(opt.buttons.ok){
15114                             handleButton("ok");
15115                         }else if(opt.buttons.yes){
15116                             handleButton("yes");
15117                         }
15118                     }
15119                 });
15120                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15121                 textareaEl.enableDisplayMode();
15122                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15123                 progressEl.enableDisplayMode();
15124                 var pf = progressEl.dom.firstChild;
15125                 if (pf) {
15126                     pp = Roo.get(pf.firstChild);
15127                     pp.setHeight(pf.offsetHeight);
15128                 }
15129                 
15130             }
15131             return dlg;
15132         },
15133
15134         /**
15135          * Updates the message box body text
15136          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15137          * the XHTML-compliant non-breaking space character '&amp;#160;')
15138          * @return {Roo.MessageBox} This message box
15139          */
15140         updateText : function(text){
15141             if(!dlg.isVisible() && !opt.width){
15142                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15143             }
15144             msgEl.innerHTML = text || '&#160;';
15145       
15146             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15147             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15148             var w = Math.max(
15149                     Math.min(opt.width || cw , this.maxWidth), 
15150                     Math.max(opt.minWidth || this.minWidth, bwidth)
15151             );
15152             if(opt.prompt){
15153                 activeTextEl.setWidth(w);
15154             }
15155             if(dlg.isVisible()){
15156                 dlg.fixedcenter = false;
15157             }
15158             // to big, make it scroll. = But as usual stupid IE does not support
15159             // !important..
15160             
15161             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15162                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15163                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15164             } else {
15165                 bodyEl.dom.style.height = '';
15166                 bodyEl.dom.style.overflowY = '';
15167             }
15168             if (cw > w) {
15169                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15170             } else {
15171                 bodyEl.dom.style.overflowX = '';
15172             }
15173             
15174             dlg.setContentSize(w, bodyEl.getHeight());
15175             if(dlg.isVisible()){
15176                 dlg.fixedcenter = true;
15177             }
15178             return this;
15179         },
15180
15181         /**
15182          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15183          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15184          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15185          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15186          * @return {Roo.MessageBox} This message box
15187          */
15188         updateProgress : function(value, text){
15189             if(text){
15190                 this.updateText(text);
15191             }
15192             if (pp) { // weird bug on my firefox - for some reason this is not defined
15193                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15194             }
15195             return this;
15196         },        
15197
15198         /**
15199          * Returns true if the message box is currently displayed
15200          * @return {Boolean} True if the message box is visible, else false
15201          */
15202         isVisible : function(){
15203             return dlg && dlg.isVisible();  
15204         },
15205
15206         /**
15207          * Hides the message box if it is displayed
15208          */
15209         hide : function(){
15210             if(this.isVisible()){
15211                 dlg.hide();
15212             }  
15213         },
15214
15215         /**
15216          * Displays a new message box, or reinitializes an existing message box, based on the config options
15217          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15218          * The following config object properties are supported:
15219          * <pre>
15220 Property    Type             Description
15221 ----------  ---------------  ------------------------------------------------------------------------------------
15222 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15223                                    closes (defaults to undefined)
15224 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15225                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15226 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15227                                    progress and wait dialogs will ignore this property and always hide the
15228                                    close button as they can only be closed programmatically.
15229 cls               String           A custom CSS class to apply to the message box element
15230 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15231                                    displayed (defaults to 75)
15232 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15233                                    function will be btn (the name of the button that was clicked, if applicable,
15234                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15235                                    Progress and wait dialogs will ignore this option since they do not respond to
15236                                    user actions and can only be closed programmatically, so any required function
15237                                    should be called by the same code after it closes the dialog.
15238 icon              String           A CSS class that provides a background image to be used as an icon for
15239                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15240 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15241 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15242 modal             Boolean          False to allow user interaction with the page while the message box is
15243                                    displayed (defaults to true)
15244 msg               String           A string that will replace the existing message box body text (defaults
15245                                    to the XHTML-compliant non-breaking space character '&#160;')
15246 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15247 progress          Boolean          True to display a progress bar (defaults to false)
15248 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15249 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15250 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15251 title             String           The title text
15252 value             String           The string value to set into the active textbox element if displayed
15253 wait              Boolean          True to display a progress bar (defaults to false)
15254 width             Number           The width of the dialog in pixels
15255 </pre>
15256          *
15257          * Example usage:
15258          * <pre><code>
15259 Roo.Msg.show({
15260    title: 'Address',
15261    msg: 'Please enter your address:',
15262    width: 300,
15263    buttons: Roo.MessageBox.OKCANCEL,
15264    multiline: true,
15265    fn: saveAddress,
15266    animEl: 'addAddressBtn'
15267 });
15268 </code></pre>
15269          * @param {Object} config Configuration options
15270          * @return {Roo.MessageBox} This message box
15271          */
15272         show : function(options)
15273         {
15274             
15275             // this causes nightmares if you show one dialog after another
15276             // especially on callbacks..
15277              
15278             if(this.isVisible()){
15279                 
15280                 this.hide();
15281                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15282                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
15283                 Roo.log("New Dialog Message:" +  options.msg )
15284                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15285                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15286                 
15287             }
15288             var d = this.getDialog();
15289             opt = options;
15290             d.setTitle(opt.title || "&#160;");
15291             d.close.setDisplayed(opt.closable !== false);
15292             activeTextEl = textboxEl;
15293             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15294             if(opt.prompt){
15295                 if(opt.multiline){
15296                     textboxEl.hide();
15297                     textareaEl.show();
15298                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15299                         opt.multiline : this.defaultTextHeight);
15300                     activeTextEl = textareaEl;
15301                 }else{
15302                     textboxEl.show();
15303                     textareaEl.hide();
15304                 }
15305             }else{
15306                 textboxEl.hide();
15307                 textareaEl.hide();
15308             }
15309             progressEl.setDisplayed(opt.progress === true);
15310             this.updateProgress(0);
15311             activeTextEl.dom.value = opt.value || "";
15312             if(opt.prompt){
15313                 dlg.setDefaultButton(activeTextEl);
15314             }else{
15315                 var bs = opt.buttons;
15316                 var db = null;
15317                 if(bs && bs.ok){
15318                     db = buttons["ok"];
15319                 }else if(bs && bs.yes){
15320                     db = buttons["yes"];
15321                 }
15322                 dlg.setDefaultButton(db);
15323             }
15324             bwidth = updateButtons(opt.buttons);
15325             this.updateText(opt.msg);
15326             if(opt.cls){
15327                 d.el.addClass(opt.cls);
15328             }
15329             d.proxyDrag = opt.proxyDrag === true;
15330             d.modal = opt.modal !== false;
15331             d.mask = opt.modal !== false ? mask : false;
15332             if(!d.isVisible()){
15333                 // force it to the end of the z-index stack so it gets a cursor in FF
15334                 document.body.appendChild(dlg.el.dom);
15335                 d.animateTarget = null;
15336                 d.show(options.animEl);
15337             }
15338             return this;
15339         },
15340
15341         /**
15342          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15343          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15344          * and closing the message box when the process is complete.
15345          * @param {String} title The title bar text
15346          * @param {String} msg The message box body text
15347          * @return {Roo.MessageBox} This message box
15348          */
15349         progress : function(title, msg){
15350             this.show({
15351                 title : title,
15352                 msg : msg,
15353                 buttons: false,
15354                 progress:true,
15355                 closable:false,
15356                 minWidth: this.minProgressWidth,
15357                 modal : true
15358             });
15359             return this;
15360         },
15361
15362         /**
15363          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15364          * If a callback function is passed it will be called after the user clicks the button, and the
15365          * id of the button that was clicked will be passed as the only parameter to the callback
15366          * (could also be the top-right close button).
15367          * @param {String} title The title bar text
15368          * @param {String} msg The message box body text
15369          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15370          * @param {Object} scope (optional) The scope of the callback function
15371          * @return {Roo.MessageBox} This message box
15372          */
15373         alert : function(title, msg, fn, scope){
15374             this.show({
15375                 title : title,
15376                 msg : msg,
15377                 buttons: this.OK,
15378                 fn: fn,
15379                 scope : scope,
15380                 modal : true
15381             });
15382             return this;
15383         },
15384
15385         /**
15386          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15387          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15388          * You are responsible for closing the message box when the process is complete.
15389          * @param {String} msg The message box body text
15390          * @param {String} title (optional) The title bar text
15391          * @return {Roo.MessageBox} This message box
15392          */
15393         wait : function(msg, title){
15394             this.show({
15395                 title : title,
15396                 msg : msg,
15397                 buttons: false,
15398                 closable:false,
15399                 progress:true,
15400                 modal:true,
15401                 width:300,
15402                 wait:true
15403             });
15404             waitTimer = Roo.TaskMgr.start({
15405                 run: function(i){
15406                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15407                 },
15408                 interval: 1000
15409             });
15410             return this;
15411         },
15412
15413         /**
15414          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15415          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15416          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15417          * @param {String} title The title bar text
15418          * @param {String} msg The message box body text
15419          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15420          * @param {Object} scope (optional) The scope of the callback function
15421          * @return {Roo.MessageBox} This message box
15422          */
15423         confirm : function(title, msg, fn, scope){
15424             this.show({
15425                 title : title,
15426                 msg : msg,
15427                 buttons: this.YESNO,
15428                 fn: fn,
15429                 scope : scope,
15430                 modal : true
15431             });
15432             return this;
15433         },
15434
15435         /**
15436          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15437          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15438          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15439          * (could also be the top-right close button) and the text that was entered will be passed as the two
15440          * parameters to the callback.
15441          * @param {String} title The title bar text
15442          * @param {String} msg The message box body text
15443          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15444          * @param {Object} scope (optional) The scope of the callback function
15445          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15446          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15447          * @return {Roo.MessageBox} This message box
15448          */
15449         prompt : function(title, msg, fn, scope, multiline){
15450             this.show({
15451                 title : title,
15452                 msg : msg,
15453                 buttons: this.OKCANCEL,
15454                 fn: fn,
15455                 minWidth:250,
15456                 scope : scope,
15457                 prompt:true,
15458                 multiline: multiline,
15459                 modal : true
15460             });
15461             return this;
15462         },
15463
15464         /**
15465          * Button config that displays a single OK button
15466          * @type Object
15467          */
15468         OK : {ok:true},
15469         /**
15470          * Button config that displays Yes and No buttons
15471          * @type Object
15472          */
15473         YESNO : {yes:true, no:true},
15474         /**
15475          * Button config that displays OK and Cancel buttons
15476          * @type Object
15477          */
15478         OKCANCEL : {ok:true, cancel:true},
15479         /**
15480          * Button config that displays Yes, No and Cancel buttons
15481          * @type Object
15482          */
15483         YESNOCANCEL : {yes:true, no:true, cancel:true},
15484
15485         /**
15486          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15487          * @type Number
15488          */
15489         defaultTextHeight : 75,
15490         /**
15491          * The maximum width in pixels of the message box (defaults to 600)
15492          * @type Number
15493          */
15494         maxWidth : 600,
15495         /**
15496          * The minimum width in pixels of the message box (defaults to 100)
15497          * @type Number
15498          */
15499         minWidth : 100,
15500         /**
15501          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15502          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15503          * @type Number
15504          */
15505         minProgressWidth : 250,
15506         /**
15507          * An object containing the default button text strings that can be overriden for localized language support.
15508          * Supported properties are: ok, cancel, yes and no.
15509          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15510          * @type Object
15511          */
15512         buttonText : {
15513             ok : "OK",
15514             cancel : "Cancel",
15515             yes : "Yes",
15516             no : "No"
15517         }
15518     };
15519 }();
15520
15521 /**
15522  * Shorthand for {@link Roo.MessageBox}
15523  */
15524 Roo.Msg = Roo.MessageBox;/*
15525  * Based on:
15526  * Ext JS Library 1.1.1
15527  * Copyright(c) 2006-2007, Ext JS, LLC.
15528  *
15529  * Originally Released Under LGPL - original licence link has changed is not relivant.
15530  *
15531  * Fork - LGPL
15532  * <script type="text/javascript">
15533  */
15534 /**
15535  * @class Roo.QuickTips
15536  * Provides attractive and customizable tooltips for any element.
15537  * @singleton
15538  */
15539 Roo.QuickTips = function(){
15540     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15541     var ce, bd, xy, dd;
15542     var visible = false, disabled = true, inited = false;
15543     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15544     
15545     var onOver = function(e){
15546         if(disabled){
15547             return;
15548         }
15549         var t = e.getTarget();
15550         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15551             return;
15552         }
15553         if(ce && t == ce.el){
15554             clearTimeout(hideProc);
15555             return;
15556         }
15557         if(t && tagEls[t.id]){
15558             tagEls[t.id].el = t;
15559             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15560             return;
15561         }
15562         var ttp, et = Roo.fly(t);
15563         var ns = cfg.namespace;
15564         if(tm.interceptTitles && t.title){
15565             ttp = t.title;
15566             t.qtip = ttp;
15567             t.removeAttribute("title");
15568             e.preventDefault();
15569         }else{
15570             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
15571         }
15572         if(ttp){
15573             showProc = show.defer(tm.showDelay, tm, [{
15574                 el: t, 
15575                 text: ttp, 
15576                 width: et.getAttributeNS(ns, cfg.width),
15577                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15578                 title: et.getAttributeNS(ns, cfg.title),
15579                     cls: et.getAttributeNS(ns, cfg.cls)
15580             }]);
15581         }
15582     };
15583     
15584     var onOut = function(e){
15585         clearTimeout(showProc);
15586         var t = e.getTarget();
15587         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15588             hideProc = setTimeout(hide, tm.hideDelay);
15589         }
15590     };
15591     
15592     var onMove = function(e){
15593         if(disabled){
15594             return;
15595         }
15596         xy = e.getXY();
15597         xy[1] += 18;
15598         if(tm.trackMouse && ce){
15599             el.setXY(xy);
15600         }
15601     };
15602     
15603     var onDown = function(e){
15604         clearTimeout(showProc);
15605         clearTimeout(hideProc);
15606         if(!e.within(el)){
15607             if(tm.hideOnClick){
15608                 hide();
15609                 tm.disable();
15610                 tm.enable.defer(100, tm);
15611             }
15612         }
15613     };
15614     
15615     var getPad = function(){
15616         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
15617     };
15618
15619     var show = function(o){
15620         if(disabled){
15621             return;
15622         }
15623         clearTimeout(dismissProc);
15624         ce = o;
15625         if(removeCls){ // in case manually hidden
15626             el.removeClass(removeCls);
15627             removeCls = null;
15628         }
15629         if(ce.cls){
15630             el.addClass(ce.cls);
15631             removeCls = ce.cls;
15632         }
15633         if(ce.title){
15634             tipTitle.update(ce.title);
15635             tipTitle.show();
15636         }else{
15637             tipTitle.update('');
15638             tipTitle.hide();
15639         }
15640         el.dom.style.width  = tm.maxWidth+'px';
15641         //tipBody.dom.style.width = '';
15642         tipBodyText.update(o.text);
15643         var p = getPad(), w = ce.width;
15644         if(!w){
15645             var td = tipBodyText.dom;
15646             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
15647             if(aw > tm.maxWidth){
15648                 w = tm.maxWidth;
15649             }else if(aw < tm.minWidth){
15650                 w = tm.minWidth;
15651             }else{
15652                 w = aw;
15653             }
15654         }
15655         //tipBody.setWidth(w);
15656         el.setWidth(parseInt(w, 10) + p);
15657         if(ce.autoHide === false){
15658             close.setDisplayed(true);
15659             if(dd){
15660                 dd.unlock();
15661             }
15662         }else{
15663             close.setDisplayed(false);
15664             if(dd){
15665                 dd.lock();
15666             }
15667         }
15668         if(xy){
15669             el.avoidY = xy[1]-18;
15670             el.setXY(xy);
15671         }
15672         if(tm.animate){
15673             el.setOpacity(.1);
15674             el.setStyle("visibility", "visible");
15675             el.fadeIn({callback: afterShow});
15676         }else{
15677             afterShow();
15678         }
15679     };
15680     
15681     var afterShow = function(){
15682         if(ce){
15683             el.show();
15684             esc.enable();
15685             if(tm.autoDismiss && ce.autoHide !== false){
15686                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
15687             }
15688         }
15689     };
15690     
15691     var hide = function(noanim){
15692         clearTimeout(dismissProc);
15693         clearTimeout(hideProc);
15694         ce = null;
15695         if(el.isVisible()){
15696             esc.disable();
15697             if(noanim !== true && tm.animate){
15698                 el.fadeOut({callback: afterHide});
15699             }else{
15700                 afterHide();
15701             } 
15702         }
15703     };
15704     
15705     var afterHide = function(){
15706         el.hide();
15707         if(removeCls){
15708             el.removeClass(removeCls);
15709             removeCls = null;
15710         }
15711     };
15712     
15713     return {
15714         /**
15715         * @cfg {Number} minWidth
15716         * The minimum width of the quick tip (defaults to 40)
15717         */
15718        minWidth : 40,
15719         /**
15720         * @cfg {Number} maxWidth
15721         * The maximum width of the quick tip (defaults to 300)
15722         */
15723        maxWidth : 300,
15724         /**
15725         * @cfg {Boolean} interceptTitles
15726         * True to automatically use the element's DOM title value if available (defaults to false)
15727         */
15728        interceptTitles : false,
15729         /**
15730         * @cfg {Boolean} trackMouse
15731         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
15732         */
15733        trackMouse : false,
15734         /**
15735         * @cfg {Boolean} hideOnClick
15736         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
15737         */
15738        hideOnClick : true,
15739         /**
15740         * @cfg {Number} showDelay
15741         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
15742         */
15743        showDelay : 500,
15744         /**
15745         * @cfg {Number} hideDelay
15746         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
15747         */
15748        hideDelay : 200,
15749         /**
15750         * @cfg {Boolean} autoHide
15751         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
15752         * Used in conjunction with hideDelay.
15753         */
15754        autoHide : true,
15755         /**
15756         * @cfg {Boolean}
15757         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
15758         * (defaults to true).  Used in conjunction with autoDismissDelay.
15759         */
15760        autoDismiss : true,
15761         /**
15762         * @cfg {Number}
15763         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
15764         */
15765        autoDismissDelay : 5000,
15766        /**
15767         * @cfg {Boolean} animate
15768         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
15769         */
15770        animate : false,
15771
15772        /**
15773         * @cfg {String} title
15774         * Title text to display (defaults to '').  This can be any valid HTML markup.
15775         */
15776         title: '',
15777        /**
15778         * @cfg {String} text
15779         * Body text to display (defaults to '').  This can be any valid HTML markup.
15780         */
15781         text : '',
15782        /**
15783         * @cfg {String} cls
15784         * A CSS class to apply to the base quick tip element (defaults to '').
15785         */
15786         cls : '',
15787        /**
15788         * @cfg {Number} width
15789         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
15790         * minWidth or maxWidth.
15791         */
15792         width : null,
15793
15794     /**
15795      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
15796      * or display QuickTips in a page.
15797      */
15798        init : function(){
15799           tm = Roo.QuickTips;
15800           cfg = tm.tagConfig;
15801           if(!inited){
15802               if(!Roo.isReady){ // allow calling of init() before onReady
15803                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
15804                   return;
15805               }
15806               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
15807               el.fxDefaults = {stopFx: true};
15808               // maximum custom styling
15809               //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>');
15810               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>');              
15811               tipTitle = el.child('h3');
15812               tipTitle.enableDisplayMode("block");
15813               tipBody = el.child('div.x-tip-bd');
15814               tipBodyText = el.child('div.x-tip-bd-inner');
15815               //bdLeft = el.child('div.x-tip-bd-left');
15816               //bdRight = el.child('div.x-tip-bd-right');
15817               close = el.child('div.x-tip-close');
15818               close.enableDisplayMode("block");
15819               close.on("click", hide);
15820               var d = Roo.get(document);
15821               d.on("mousedown", onDown);
15822               d.on("mouseover", onOver);
15823               d.on("mouseout", onOut);
15824               d.on("mousemove", onMove);
15825               esc = d.addKeyListener(27, hide);
15826               esc.disable();
15827               if(Roo.dd.DD){
15828                   dd = el.initDD("default", null, {
15829                       onDrag : function(){
15830                           el.sync();  
15831                       }
15832                   });
15833                   dd.setHandleElId(tipTitle.id);
15834                   dd.lock();
15835               }
15836               inited = true;
15837           }
15838           this.enable(); 
15839        },
15840
15841     /**
15842      * Configures a new quick tip instance and assigns it to a target element.  The following config options
15843      * are supported:
15844      * <pre>
15845 Property    Type                   Description
15846 ----------  ---------------------  ------------------------------------------------------------------------
15847 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
15848      * </ul>
15849      * @param {Object} config The config object
15850      */
15851        register : function(config){
15852            var cs = config instanceof Array ? config : arguments;
15853            for(var i = 0, len = cs.length; i < len; i++) {
15854                var c = cs[i];
15855                var target = c.target;
15856                if(target){
15857                    if(target instanceof Array){
15858                        for(var j = 0, jlen = target.length; j < jlen; j++){
15859                            tagEls[target[j]] = c;
15860                        }
15861                    }else{
15862                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
15863                    }
15864                }
15865            }
15866        },
15867
15868     /**
15869      * Removes this quick tip from its element and destroys it.
15870      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
15871      */
15872        unregister : function(el){
15873            delete tagEls[Roo.id(el)];
15874        },
15875
15876     /**
15877      * Enable this quick tip.
15878      */
15879        enable : function(){
15880            if(inited && disabled){
15881                locks.pop();
15882                if(locks.length < 1){
15883                    disabled = false;
15884                }
15885            }
15886        },
15887
15888     /**
15889      * Disable this quick tip.
15890      */
15891        disable : function(){
15892           disabled = true;
15893           clearTimeout(showProc);
15894           clearTimeout(hideProc);
15895           clearTimeout(dismissProc);
15896           if(ce){
15897               hide(true);
15898           }
15899           locks.push(1);
15900        },
15901
15902     /**
15903      * Returns true if the quick tip is enabled, else false.
15904      */
15905        isEnabled : function(){
15906             return !disabled;
15907        },
15908
15909         // private
15910        tagConfig : {
15911            namespace : "ext",
15912            attribute : "qtip",
15913            width : "width",
15914            target : "target",
15915            title : "qtitle",
15916            hide : "hide",
15917            cls : "qclass"
15918        }
15919    };
15920 }();
15921
15922 // backwards compat
15923 Roo.QuickTips.tips = Roo.QuickTips.register;/*
15924  * Based on:
15925  * Ext JS Library 1.1.1
15926  * Copyright(c) 2006-2007, Ext JS, LLC.
15927  *
15928  * Originally Released Under LGPL - original licence link has changed is not relivant.
15929  *
15930  * Fork - LGPL
15931  * <script type="text/javascript">
15932  */
15933  
15934
15935 /**
15936  * @class Roo.tree.TreePanel
15937  * @extends Roo.data.Tree
15938
15939  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
15940  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
15941  * @cfg {Boolean} enableDD true to enable drag and drop
15942  * @cfg {Boolean} enableDrag true to enable just drag
15943  * @cfg {Boolean} enableDrop true to enable just drop
15944  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
15945  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
15946  * @cfg {String} ddGroup The DD group this TreePanel belongs to
15947  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
15948  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
15949  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
15950  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
15951  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
15952  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
15953  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
15954  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
15955  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
15956  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
15957  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
15958  * @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>
15959  * @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>
15960  * 
15961  * @constructor
15962  * @param {String/HTMLElement/Element} el The container element
15963  * @param {Object} config
15964  */
15965 Roo.tree.TreePanel = function(el, config){
15966     var root = false;
15967     var loader = false;
15968     if (config.root) {
15969         root = config.root;
15970         delete config.root;
15971     }
15972     if (config.loader) {
15973         loader = config.loader;
15974         delete config.loader;
15975     }
15976     
15977     Roo.apply(this, config);
15978     Roo.tree.TreePanel.superclass.constructor.call(this);
15979     this.el = Roo.get(el);
15980     this.el.addClass('x-tree');
15981     //console.log(root);
15982     if (root) {
15983         this.setRootNode( Roo.factory(root, Roo.tree));
15984     }
15985     if (loader) {
15986         this.loader = Roo.factory(loader, Roo.tree);
15987     }
15988    /**
15989     * Read-only. The id of the container element becomes this TreePanel's id.
15990     */
15991     this.id = this.el.id;
15992     this.addEvents({
15993         /**
15994         * @event beforeload
15995         * Fires before a node is loaded, return false to cancel
15996         * @param {Node} node The node being loaded
15997         */
15998         "beforeload" : true,
15999         /**
16000         * @event load
16001         * Fires when a node is loaded
16002         * @param {Node} node The node that was loaded
16003         */
16004         "load" : true,
16005         /**
16006         * @event textchange
16007         * Fires when the text for a node is changed
16008         * @param {Node} node The node
16009         * @param {String} text The new text
16010         * @param {String} oldText The old text
16011         */
16012         "textchange" : true,
16013         /**
16014         * @event beforeexpand
16015         * Fires before a node is expanded, return false to cancel.
16016         * @param {Node} node The node
16017         * @param {Boolean} deep
16018         * @param {Boolean} anim
16019         */
16020         "beforeexpand" : true,
16021         /**
16022         * @event beforecollapse
16023         * Fires before a node is collapsed, return false to cancel.
16024         * @param {Node} node The node
16025         * @param {Boolean} deep
16026         * @param {Boolean} anim
16027         */
16028         "beforecollapse" : true,
16029         /**
16030         * @event expand
16031         * Fires when a node is expanded
16032         * @param {Node} node The node
16033         */
16034         "expand" : true,
16035         /**
16036         * @event disabledchange
16037         * Fires when the disabled status of a node changes
16038         * @param {Node} node The node
16039         * @param {Boolean} disabled
16040         */
16041         "disabledchange" : true,
16042         /**
16043         * @event collapse
16044         * Fires when a node is collapsed
16045         * @param {Node} node The node
16046         */
16047         "collapse" : true,
16048         /**
16049         * @event beforeclick
16050         * Fires before click processing on a node. Return false to cancel the default action.
16051         * @param {Node} node The node
16052         * @param {Roo.EventObject} e The event object
16053         */
16054         "beforeclick":true,
16055         /**
16056         * @event checkchange
16057         * Fires when a node with a checkbox's checked property changes
16058         * @param {Node} this This node
16059         * @param {Boolean} checked
16060         */
16061         "checkchange":true,
16062         /**
16063         * @event click
16064         * Fires when a node is clicked
16065         * @param {Node} node The node
16066         * @param {Roo.EventObject} e The event object
16067         */
16068         "click":true,
16069         /**
16070         * @event dblclick
16071         * Fires when a node is double clicked
16072         * @param {Node} node The node
16073         * @param {Roo.EventObject} e The event object
16074         */
16075         "dblclick":true,
16076         /**
16077         * @event contextmenu
16078         * Fires when a node is right clicked
16079         * @param {Node} node The node
16080         * @param {Roo.EventObject} e The event object
16081         */
16082         "contextmenu":true,
16083         /**
16084         * @event beforechildrenrendered
16085         * Fires right before the child nodes for a node are rendered
16086         * @param {Node} node The node
16087         */
16088         "beforechildrenrendered":true,
16089         /**
16090         * @event startdrag
16091         * Fires when a node starts being dragged
16092         * @param {Roo.tree.TreePanel} this
16093         * @param {Roo.tree.TreeNode} node
16094         * @param {event} e The raw browser event
16095         */ 
16096        "startdrag" : true,
16097        /**
16098         * @event enddrag
16099         * Fires when a drag operation is complete
16100         * @param {Roo.tree.TreePanel} this
16101         * @param {Roo.tree.TreeNode} node
16102         * @param {event} e The raw browser event
16103         */
16104        "enddrag" : true,
16105        /**
16106         * @event dragdrop
16107         * Fires when a dragged node is dropped on a valid DD target
16108         * @param {Roo.tree.TreePanel} this
16109         * @param {Roo.tree.TreeNode} node
16110         * @param {DD} dd The dd it was dropped on
16111         * @param {event} e The raw browser event
16112         */
16113        "dragdrop" : true,
16114        /**
16115         * @event beforenodedrop
16116         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16117         * passed to handlers has the following properties:<br />
16118         * <ul style="padding:5px;padding-left:16px;">
16119         * <li>tree - The TreePanel</li>
16120         * <li>target - The node being targeted for the drop</li>
16121         * <li>data - The drag data from the drag source</li>
16122         * <li>point - The point of the drop - append, above or below</li>
16123         * <li>source - The drag source</li>
16124         * <li>rawEvent - Raw mouse event</li>
16125         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16126         * to be inserted by setting them on this object.</li>
16127         * <li>cancel - Set this to true to cancel the drop.</li>
16128         * </ul>
16129         * @param {Object} dropEvent
16130         */
16131        "beforenodedrop" : true,
16132        /**
16133         * @event nodedrop
16134         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16135         * passed to handlers has the following properties:<br />
16136         * <ul style="padding:5px;padding-left:16px;">
16137         * <li>tree - The TreePanel</li>
16138         * <li>target - The node being targeted for the drop</li>
16139         * <li>data - The drag data from the drag source</li>
16140         * <li>point - The point of the drop - append, above or below</li>
16141         * <li>source - The drag source</li>
16142         * <li>rawEvent - Raw mouse event</li>
16143         * <li>dropNode - Dropped node(s).</li>
16144         * </ul>
16145         * @param {Object} dropEvent
16146         */
16147        "nodedrop" : true,
16148         /**
16149         * @event nodedragover
16150         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16151         * passed to handlers has the following properties:<br />
16152         * <ul style="padding:5px;padding-left:16px;">
16153         * <li>tree - The TreePanel</li>
16154         * <li>target - The node being targeted for the drop</li>
16155         * <li>data - The drag data from the drag source</li>
16156         * <li>point - The point of the drop - append, above or below</li>
16157         * <li>source - The drag source</li>
16158         * <li>rawEvent - Raw mouse event</li>
16159         * <li>dropNode - Drop node(s) provided by the source.</li>
16160         * <li>cancel - Set this to true to signal drop not allowed.</li>
16161         * </ul>
16162         * @param {Object} dragOverEvent
16163         */
16164        "nodedragover" : true
16165         
16166     });
16167     if(this.singleExpand){
16168        this.on("beforeexpand", this.restrictExpand, this);
16169     }
16170     if (this.editor) {
16171         this.editor.tree = this;
16172         this.editor = Roo.factory(this.editor, Roo.tree);
16173     }
16174     
16175     if (this.selModel) {
16176         this.selModel = Roo.factory(this.selModel, Roo.tree);
16177     }
16178    
16179 };
16180 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16181     rootVisible : true,
16182     animate: Roo.enableFx,
16183     lines : true,
16184     enableDD : false,
16185     hlDrop : Roo.enableFx,
16186   
16187     renderer: false,
16188     
16189     rendererTip: false,
16190     // private
16191     restrictExpand : function(node){
16192         var p = node.parentNode;
16193         if(p){
16194             if(p.expandedChild && p.expandedChild.parentNode == p){
16195                 p.expandedChild.collapse();
16196             }
16197             p.expandedChild = node;
16198         }
16199     },
16200
16201     // private override
16202     setRootNode : function(node){
16203         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16204         if(!this.rootVisible){
16205             node.ui = new Roo.tree.RootTreeNodeUI(node);
16206         }
16207         return node;
16208     },
16209
16210     /**
16211      * Returns the container element for this TreePanel
16212      */
16213     getEl : function(){
16214         return this.el;
16215     },
16216
16217     /**
16218      * Returns the default TreeLoader for this TreePanel
16219      */
16220     getLoader : function(){
16221         return this.loader;
16222     },
16223
16224     /**
16225      * Expand all nodes
16226      */
16227     expandAll : function(){
16228         this.root.expand(true);
16229     },
16230
16231     /**
16232      * Collapse all nodes
16233      */
16234     collapseAll : function(){
16235         this.root.collapse(true);
16236     },
16237
16238     /**
16239      * Returns the selection model used by this TreePanel
16240      */
16241     getSelectionModel : function(){
16242         if(!this.selModel){
16243             this.selModel = new Roo.tree.DefaultSelectionModel();
16244         }
16245         return this.selModel;
16246     },
16247
16248     /**
16249      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16250      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16251      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16252      * @return {Array}
16253      */
16254     getChecked : function(a, startNode){
16255         startNode = startNode || this.root;
16256         var r = [];
16257         var f = function(){
16258             if(this.attributes.checked){
16259                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16260             }
16261         }
16262         startNode.cascade(f);
16263         return r;
16264     },
16265
16266     /**
16267      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16268      * @param {String} path
16269      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16270      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16271      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16272      */
16273     expandPath : function(path, attr, callback){
16274         attr = attr || "id";
16275         var keys = path.split(this.pathSeparator);
16276         var curNode = this.root;
16277         if(curNode.attributes[attr] != keys[1]){ // invalid root
16278             if(callback){
16279                 callback(false, null);
16280             }
16281             return;
16282         }
16283         var index = 1;
16284         var f = function(){
16285             if(++index == keys.length){
16286                 if(callback){
16287                     callback(true, curNode);
16288                 }
16289                 return;
16290             }
16291             var c = curNode.findChild(attr, keys[index]);
16292             if(!c){
16293                 if(callback){
16294                     callback(false, curNode);
16295                 }
16296                 return;
16297             }
16298             curNode = c;
16299             c.expand(false, false, f);
16300         };
16301         curNode.expand(false, false, f);
16302     },
16303
16304     /**
16305      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16306      * @param {String} path
16307      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16308      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16309      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16310      */
16311     selectPath : function(path, attr, callback){
16312         attr = attr || "id";
16313         var keys = path.split(this.pathSeparator);
16314         var v = keys.pop();
16315         if(keys.length > 0){
16316             var f = function(success, node){
16317                 if(success && node){
16318                     var n = node.findChild(attr, v);
16319                     if(n){
16320                         n.select();
16321                         if(callback){
16322                             callback(true, n);
16323                         }
16324                     }else if(callback){
16325                         callback(false, n);
16326                     }
16327                 }else{
16328                     if(callback){
16329                         callback(false, n);
16330                     }
16331                 }
16332             };
16333             this.expandPath(keys.join(this.pathSeparator), attr, f);
16334         }else{
16335             this.root.select();
16336             if(callback){
16337                 callback(true, this.root);
16338             }
16339         }
16340     },
16341
16342     getTreeEl : function(){
16343         return this.el;
16344     },
16345
16346     /**
16347      * Trigger rendering of this TreePanel
16348      */
16349     render : function(){
16350         if (this.innerCt) {
16351             return this; // stop it rendering more than once!!
16352         }
16353         
16354         this.innerCt = this.el.createChild({tag:"ul",
16355                cls:"x-tree-root-ct " +
16356                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16357
16358         if(this.containerScroll){
16359             Roo.dd.ScrollManager.register(this.el);
16360         }
16361         if((this.enableDD || this.enableDrop) && !this.dropZone){
16362            /**
16363             * The dropZone used by this tree if drop is enabled
16364             * @type Roo.tree.TreeDropZone
16365             */
16366              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16367                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16368            });
16369         }
16370         if((this.enableDD || this.enableDrag) && !this.dragZone){
16371            /**
16372             * The dragZone used by this tree if drag is enabled
16373             * @type Roo.tree.TreeDragZone
16374             */
16375             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16376                ddGroup: this.ddGroup || "TreeDD",
16377                scroll: this.ddScroll
16378            });
16379         }
16380         this.getSelectionModel().init(this);
16381         if (!this.root) {
16382             Roo.log("ROOT not set in tree");
16383             return this;
16384         }
16385         this.root.render();
16386         if(!this.rootVisible){
16387             this.root.renderChildren();
16388         }
16389         return this;
16390     }
16391 });/*
16392  * Based on:
16393  * Ext JS Library 1.1.1
16394  * Copyright(c) 2006-2007, Ext JS, LLC.
16395  *
16396  * Originally Released Under LGPL - original licence link has changed is not relivant.
16397  *
16398  * Fork - LGPL
16399  * <script type="text/javascript">
16400  */
16401  
16402
16403 /**
16404  * @class Roo.tree.DefaultSelectionModel
16405  * @extends Roo.util.Observable
16406  * The default single selection for a TreePanel.
16407  * @param {Object} cfg Configuration
16408  */
16409 Roo.tree.DefaultSelectionModel = function(cfg){
16410    this.selNode = null;
16411    
16412    
16413    
16414    this.addEvents({
16415        /**
16416         * @event selectionchange
16417         * Fires when the selected node changes
16418         * @param {DefaultSelectionModel} this
16419         * @param {TreeNode} node the new selection
16420         */
16421        "selectionchange" : true,
16422
16423        /**
16424         * @event beforeselect
16425         * Fires before the selected node changes, return false to cancel the change
16426         * @param {DefaultSelectionModel} this
16427         * @param {TreeNode} node the new selection
16428         * @param {TreeNode} node the old selection
16429         */
16430        "beforeselect" : true
16431    });
16432    
16433     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
16434 };
16435
16436 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16437     init : function(tree){
16438         this.tree = tree;
16439         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16440         tree.on("click", this.onNodeClick, this);
16441     },
16442     
16443     onNodeClick : function(node, e){
16444         if (e.ctrlKey && this.selNode == node)  {
16445             this.unselect(node);
16446             return;
16447         }
16448         this.select(node);
16449     },
16450     
16451     /**
16452      * Select a node.
16453      * @param {TreeNode} node The node to select
16454      * @return {TreeNode} The selected node
16455      */
16456     select : function(node){
16457         var last = this.selNode;
16458         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16459             if(last){
16460                 last.ui.onSelectedChange(false);
16461             }
16462             this.selNode = node;
16463             node.ui.onSelectedChange(true);
16464             this.fireEvent("selectionchange", this, node, last);
16465         }
16466         return node;
16467     },
16468     
16469     /**
16470      * Deselect a node.
16471      * @param {TreeNode} node The node to unselect
16472      */
16473     unselect : function(node){
16474         if(this.selNode == node){
16475             this.clearSelections();
16476         }    
16477     },
16478     
16479     /**
16480      * Clear all selections
16481      */
16482     clearSelections : function(){
16483         var n = this.selNode;
16484         if(n){
16485             n.ui.onSelectedChange(false);
16486             this.selNode = null;
16487             this.fireEvent("selectionchange", this, null);
16488         }
16489         return n;
16490     },
16491     
16492     /**
16493      * Get the selected node
16494      * @return {TreeNode} The selected node
16495      */
16496     getSelectedNode : function(){
16497         return this.selNode;    
16498     },
16499     
16500     /**
16501      * Returns true if the node is selected
16502      * @param {TreeNode} node The node to check
16503      * @return {Boolean}
16504      */
16505     isSelected : function(node){
16506         return this.selNode == node;  
16507     },
16508
16509     /**
16510      * Selects the node above the selected node in the tree, intelligently walking the nodes
16511      * @return TreeNode The new selection
16512      */
16513     selectPrevious : function(){
16514         var s = this.selNode || this.lastSelNode;
16515         if(!s){
16516             return null;
16517         }
16518         var ps = s.previousSibling;
16519         if(ps){
16520             if(!ps.isExpanded() || ps.childNodes.length < 1){
16521                 return this.select(ps);
16522             } else{
16523                 var lc = ps.lastChild;
16524                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16525                     lc = lc.lastChild;
16526                 }
16527                 return this.select(lc);
16528             }
16529         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16530             return this.select(s.parentNode);
16531         }
16532         return null;
16533     },
16534
16535     /**
16536      * Selects the node above the selected node in the tree, intelligently walking the nodes
16537      * @return TreeNode The new selection
16538      */
16539     selectNext : function(){
16540         var s = this.selNode || this.lastSelNode;
16541         if(!s){
16542             return null;
16543         }
16544         if(s.firstChild && s.isExpanded()){
16545              return this.select(s.firstChild);
16546          }else if(s.nextSibling){
16547              return this.select(s.nextSibling);
16548          }else if(s.parentNode){
16549             var newS = null;
16550             s.parentNode.bubble(function(){
16551                 if(this.nextSibling){
16552                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16553                     return false;
16554                 }
16555             });
16556             return newS;
16557          }
16558         return null;
16559     },
16560
16561     onKeyDown : function(e){
16562         var s = this.selNode || this.lastSelNode;
16563         // undesirable, but required
16564         var sm = this;
16565         if(!s){
16566             return;
16567         }
16568         var k = e.getKey();
16569         switch(k){
16570              case e.DOWN:
16571                  e.stopEvent();
16572                  this.selectNext();
16573              break;
16574              case e.UP:
16575                  e.stopEvent();
16576                  this.selectPrevious();
16577              break;
16578              case e.RIGHT:
16579                  e.preventDefault();
16580                  if(s.hasChildNodes()){
16581                      if(!s.isExpanded()){
16582                          s.expand();
16583                      }else if(s.firstChild){
16584                          this.select(s.firstChild, e);
16585                      }
16586                  }
16587              break;
16588              case e.LEFT:
16589                  e.preventDefault();
16590                  if(s.hasChildNodes() && s.isExpanded()){
16591                      s.collapse();
16592                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16593                      this.select(s.parentNode, e);
16594                  }
16595              break;
16596         };
16597     }
16598 });
16599
16600 /**
16601  * @class Roo.tree.MultiSelectionModel
16602  * @extends Roo.util.Observable
16603  * Multi selection for a TreePanel.
16604  * @param {Object} cfg Configuration
16605  */
16606 Roo.tree.MultiSelectionModel = function(){
16607    this.selNodes = [];
16608    this.selMap = {};
16609    this.addEvents({
16610        /**
16611         * @event selectionchange
16612         * Fires when the selected nodes change
16613         * @param {MultiSelectionModel} this
16614         * @param {Array} nodes Array of the selected nodes
16615         */
16616        "selectionchange" : true
16617    });
16618    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
16619    
16620 };
16621
16622 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16623     init : function(tree){
16624         this.tree = tree;
16625         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16626         tree.on("click", this.onNodeClick, this);
16627     },
16628     
16629     onNodeClick : function(node, e){
16630         this.select(node, e, e.ctrlKey);
16631     },
16632     
16633     /**
16634      * Select a node.
16635      * @param {TreeNode} node The node to select
16636      * @param {EventObject} e (optional) An event associated with the selection
16637      * @param {Boolean} keepExisting True to retain existing selections
16638      * @return {TreeNode} The selected node
16639      */
16640     select : function(node, e, keepExisting){
16641         if(keepExisting !== true){
16642             this.clearSelections(true);
16643         }
16644         if(this.isSelected(node)){
16645             this.lastSelNode = node;
16646             return node;
16647         }
16648         this.selNodes.push(node);
16649         this.selMap[node.id] = node;
16650         this.lastSelNode = node;
16651         node.ui.onSelectedChange(true);
16652         this.fireEvent("selectionchange", this, this.selNodes);
16653         return node;
16654     },
16655     
16656     /**
16657      * Deselect a node.
16658      * @param {TreeNode} node The node to unselect
16659      */
16660     unselect : function(node){
16661         if(this.selMap[node.id]){
16662             node.ui.onSelectedChange(false);
16663             var sn = this.selNodes;
16664             var index = -1;
16665             if(sn.indexOf){
16666                 index = sn.indexOf(node);
16667             }else{
16668                 for(var i = 0, len = sn.length; i < len; i++){
16669                     if(sn[i] == node){
16670                         index = i;
16671                         break;
16672                     }
16673                 }
16674             }
16675             if(index != -1){
16676                 this.selNodes.splice(index, 1);
16677             }
16678             delete this.selMap[node.id];
16679             this.fireEvent("selectionchange", this, this.selNodes);
16680         }
16681     },
16682     
16683     /**
16684      * Clear all selections
16685      */
16686     clearSelections : function(suppressEvent){
16687         var sn = this.selNodes;
16688         if(sn.length > 0){
16689             for(var i = 0, len = sn.length; i < len; i++){
16690                 sn[i].ui.onSelectedChange(false);
16691             }
16692             this.selNodes = [];
16693             this.selMap = {};
16694             if(suppressEvent !== true){
16695                 this.fireEvent("selectionchange", this, this.selNodes);
16696             }
16697         }
16698     },
16699     
16700     /**
16701      * Returns true if the node is selected
16702      * @param {TreeNode} node The node to check
16703      * @return {Boolean}
16704      */
16705     isSelected : function(node){
16706         return this.selMap[node.id] ? true : false;  
16707     },
16708     
16709     /**
16710      * Returns an array of the selected nodes
16711      * @return {Array}
16712      */
16713     getSelectedNodes : function(){
16714         return this.selNodes;    
16715     },
16716
16717     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
16718
16719     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
16720
16721     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
16722 });/*
16723  * Based on:
16724  * Ext JS Library 1.1.1
16725  * Copyright(c) 2006-2007, Ext JS, LLC.
16726  *
16727  * Originally Released Under LGPL - original licence link has changed is not relivant.
16728  *
16729  * Fork - LGPL
16730  * <script type="text/javascript">
16731  */
16732  
16733 /**
16734  * @class Roo.tree.TreeNode
16735  * @extends Roo.data.Node
16736  * @cfg {String} text The text for this node
16737  * @cfg {Boolean} expanded true to start the node expanded
16738  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
16739  * @cfg {Boolean} allowDrop false if this node cannot be drop on
16740  * @cfg {Boolean} disabled true to start the node disabled
16741  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
16742  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
16743  * @cfg {String} cls A css class to be added to the node
16744  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
16745  * @cfg {String} href URL of the link used for the node (defaults to #)
16746  * @cfg {String} hrefTarget target frame for the link
16747  * @cfg {String} qtip An Ext QuickTip for the node
16748  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
16749  * @cfg {Boolean} singleClickExpand True for single click expand on this node
16750  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
16751  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
16752  * (defaults to undefined with no checkbox rendered)
16753  * @constructor
16754  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
16755  */
16756 Roo.tree.TreeNode = function(attributes){
16757     attributes = attributes || {};
16758     if(typeof attributes == "string"){
16759         attributes = {text: attributes};
16760     }
16761     this.childrenRendered = false;
16762     this.rendered = false;
16763     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
16764     this.expanded = attributes.expanded === true;
16765     this.isTarget = attributes.isTarget !== false;
16766     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
16767     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
16768
16769     /**
16770      * Read-only. The text for this node. To change it use setText().
16771      * @type String
16772      */
16773     this.text = attributes.text;
16774     /**
16775      * True if this node is disabled.
16776      * @type Boolean
16777      */
16778     this.disabled = attributes.disabled === true;
16779
16780     this.addEvents({
16781         /**
16782         * @event textchange
16783         * Fires when the text for this node is changed
16784         * @param {Node} this This node
16785         * @param {String} text The new text
16786         * @param {String} oldText The old text
16787         */
16788         "textchange" : true,
16789         /**
16790         * @event beforeexpand
16791         * Fires before this node is expanded, return false to cancel.
16792         * @param {Node} this This node
16793         * @param {Boolean} deep
16794         * @param {Boolean} anim
16795         */
16796         "beforeexpand" : true,
16797         /**
16798         * @event beforecollapse
16799         * Fires before this node is collapsed, return false to cancel.
16800         * @param {Node} this This node
16801         * @param {Boolean} deep
16802         * @param {Boolean} anim
16803         */
16804         "beforecollapse" : true,
16805         /**
16806         * @event expand
16807         * Fires when this node is expanded
16808         * @param {Node} this This node
16809         */
16810         "expand" : true,
16811         /**
16812         * @event disabledchange
16813         * Fires when the disabled status of this node changes
16814         * @param {Node} this This node
16815         * @param {Boolean} disabled
16816         */
16817         "disabledchange" : true,
16818         /**
16819         * @event collapse
16820         * Fires when this node is collapsed
16821         * @param {Node} this This node
16822         */
16823         "collapse" : true,
16824         /**
16825         * @event beforeclick
16826         * Fires before click processing. Return false to cancel the default action.
16827         * @param {Node} this This node
16828         * @param {Roo.EventObject} e The event object
16829         */
16830         "beforeclick":true,
16831         /**
16832         * @event checkchange
16833         * Fires when a node with a checkbox's checked property changes
16834         * @param {Node} this This node
16835         * @param {Boolean} checked
16836         */
16837         "checkchange":true,
16838         /**
16839         * @event click
16840         * Fires when this node is clicked
16841         * @param {Node} this This node
16842         * @param {Roo.EventObject} e The event object
16843         */
16844         "click":true,
16845         /**
16846         * @event dblclick
16847         * Fires when this node is double clicked
16848         * @param {Node} this This node
16849         * @param {Roo.EventObject} e The event object
16850         */
16851         "dblclick":true,
16852         /**
16853         * @event contextmenu
16854         * Fires when this node is right clicked
16855         * @param {Node} this This node
16856         * @param {Roo.EventObject} e The event object
16857         */
16858         "contextmenu":true,
16859         /**
16860         * @event beforechildrenrendered
16861         * Fires right before the child nodes for this node are rendered
16862         * @param {Node} this This node
16863         */
16864         "beforechildrenrendered":true
16865     });
16866
16867     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
16868
16869     /**
16870      * Read-only. The UI for this node
16871      * @type TreeNodeUI
16872      */
16873     this.ui = new uiClass(this);
16874     
16875     // finally support items[]
16876     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
16877         return;
16878     }
16879     
16880     
16881     Roo.each(this.attributes.items, function(c) {
16882         this.appendChild(Roo.factory(c,Roo.Tree));
16883     }, this);
16884     delete this.attributes.items;
16885     
16886     
16887     
16888 };
16889 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
16890     preventHScroll: true,
16891     /**
16892      * Returns true if this node is expanded
16893      * @return {Boolean}
16894      */
16895     isExpanded : function(){
16896         return this.expanded;
16897     },
16898
16899     /**
16900      * Returns the UI object for this node
16901      * @return {TreeNodeUI}
16902      */
16903     getUI : function(){
16904         return this.ui;
16905     },
16906
16907     // private override
16908     setFirstChild : function(node){
16909         var of = this.firstChild;
16910         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
16911         if(this.childrenRendered && of && node != of){
16912             of.renderIndent(true, true);
16913         }
16914         if(this.rendered){
16915             this.renderIndent(true, true);
16916         }
16917     },
16918
16919     // private override
16920     setLastChild : function(node){
16921         var ol = this.lastChild;
16922         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
16923         if(this.childrenRendered && ol && node != ol){
16924             ol.renderIndent(true, true);
16925         }
16926         if(this.rendered){
16927             this.renderIndent(true, true);
16928         }
16929     },
16930
16931     // these methods are overridden to provide lazy rendering support
16932     // private override
16933     appendChild : function()
16934     {
16935         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
16936         if(node && this.childrenRendered){
16937             node.render();
16938         }
16939         this.ui.updateExpandIcon();
16940         return node;
16941     },
16942
16943     // private override
16944     removeChild : function(node){
16945         this.ownerTree.getSelectionModel().unselect(node);
16946         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
16947         // if it's been rendered remove dom node
16948         if(this.childrenRendered){
16949             node.ui.remove();
16950         }
16951         if(this.childNodes.length < 1){
16952             this.collapse(false, false);
16953         }else{
16954             this.ui.updateExpandIcon();
16955         }
16956         if(!this.firstChild) {
16957             this.childrenRendered = false;
16958         }
16959         return node;
16960     },
16961
16962     // private override
16963     insertBefore : function(node, refNode){
16964         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
16965         if(newNode && refNode && this.childrenRendered){
16966             node.render();
16967         }
16968         this.ui.updateExpandIcon();
16969         return newNode;
16970     },
16971
16972     /**
16973      * Sets the text for this node
16974      * @param {String} text
16975      */
16976     setText : function(text){
16977         var oldText = this.text;
16978         this.text = text;
16979         this.attributes.text = text;
16980         if(this.rendered){ // event without subscribing
16981             this.ui.onTextChange(this, text, oldText);
16982         }
16983         this.fireEvent("textchange", this, text, oldText);
16984     },
16985
16986     /**
16987      * Triggers selection of this node
16988      */
16989     select : function(){
16990         this.getOwnerTree().getSelectionModel().select(this);
16991     },
16992
16993     /**
16994      * Triggers deselection of this node
16995      */
16996     unselect : function(){
16997         this.getOwnerTree().getSelectionModel().unselect(this);
16998     },
16999
17000     /**
17001      * Returns true if this node is selected
17002      * @return {Boolean}
17003      */
17004     isSelected : function(){
17005         return this.getOwnerTree().getSelectionModel().isSelected(this);
17006     },
17007
17008     /**
17009      * Expand this node.
17010      * @param {Boolean} deep (optional) True to expand all children as well
17011      * @param {Boolean} anim (optional) false to cancel the default animation
17012      * @param {Function} callback (optional) A callback to be called when
17013      * expanding this node completes (does not wait for deep expand to complete).
17014      * Called with 1 parameter, this node.
17015      */
17016     expand : function(deep, anim, callback){
17017         if(!this.expanded){
17018             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17019                 return;
17020             }
17021             if(!this.childrenRendered){
17022                 this.renderChildren();
17023             }
17024             this.expanded = true;
17025             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17026                 this.ui.animExpand(function(){
17027                     this.fireEvent("expand", this);
17028                     if(typeof callback == "function"){
17029                         callback(this);
17030                     }
17031                     if(deep === true){
17032                         this.expandChildNodes(true);
17033                     }
17034                 }.createDelegate(this));
17035                 return;
17036             }else{
17037                 this.ui.expand();
17038                 this.fireEvent("expand", this);
17039                 if(typeof callback == "function"){
17040                     callback(this);
17041                 }
17042             }
17043         }else{
17044            if(typeof callback == "function"){
17045                callback(this);
17046            }
17047         }
17048         if(deep === true){
17049             this.expandChildNodes(true);
17050         }
17051     },
17052
17053     isHiddenRoot : function(){
17054         return this.isRoot && !this.getOwnerTree().rootVisible;
17055     },
17056
17057     /**
17058      * Collapse this node.
17059      * @param {Boolean} deep (optional) True to collapse all children as well
17060      * @param {Boolean} anim (optional) false to cancel the default animation
17061      */
17062     collapse : function(deep, anim){
17063         if(this.expanded && !this.isHiddenRoot()){
17064             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17065                 return;
17066             }
17067             this.expanded = false;
17068             if((this.getOwnerTree().animate && anim !== false) || anim){
17069                 this.ui.animCollapse(function(){
17070                     this.fireEvent("collapse", this);
17071                     if(deep === true){
17072                         this.collapseChildNodes(true);
17073                     }
17074                 }.createDelegate(this));
17075                 return;
17076             }else{
17077                 this.ui.collapse();
17078                 this.fireEvent("collapse", this);
17079             }
17080         }
17081         if(deep === true){
17082             var cs = this.childNodes;
17083             for(var i = 0, len = cs.length; i < len; i++) {
17084                 cs[i].collapse(true, false);
17085             }
17086         }
17087     },
17088
17089     // private
17090     delayedExpand : function(delay){
17091         if(!this.expandProcId){
17092             this.expandProcId = this.expand.defer(delay, this);
17093         }
17094     },
17095
17096     // private
17097     cancelExpand : function(){
17098         if(this.expandProcId){
17099             clearTimeout(this.expandProcId);
17100         }
17101         this.expandProcId = false;
17102     },
17103
17104     /**
17105      * Toggles expanded/collapsed state of the node
17106      */
17107     toggle : function(){
17108         if(this.expanded){
17109             this.collapse();
17110         }else{
17111             this.expand();
17112         }
17113     },
17114
17115     /**
17116      * Ensures all parent nodes are expanded
17117      */
17118     ensureVisible : function(callback){
17119         var tree = this.getOwnerTree();
17120         tree.expandPath(this.parentNode.getPath(), false, function(){
17121             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17122             Roo.callback(callback);
17123         }.createDelegate(this));
17124     },
17125
17126     /**
17127      * Expand all child nodes
17128      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17129      */
17130     expandChildNodes : function(deep){
17131         var cs = this.childNodes;
17132         for(var i = 0, len = cs.length; i < len; i++) {
17133                 cs[i].expand(deep);
17134         }
17135     },
17136
17137     /**
17138      * Collapse all child nodes
17139      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17140      */
17141     collapseChildNodes : function(deep){
17142         var cs = this.childNodes;
17143         for(var i = 0, len = cs.length; i < len; i++) {
17144                 cs[i].collapse(deep);
17145         }
17146     },
17147
17148     /**
17149      * Disables this node
17150      */
17151     disable : function(){
17152         this.disabled = true;
17153         this.unselect();
17154         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17155             this.ui.onDisableChange(this, true);
17156         }
17157         this.fireEvent("disabledchange", this, true);
17158     },
17159
17160     /**
17161      * Enables this node
17162      */
17163     enable : function(){
17164         this.disabled = false;
17165         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17166             this.ui.onDisableChange(this, false);
17167         }
17168         this.fireEvent("disabledchange", this, false);
17169     },
17170
17171     // private
17172     renderChildren : function(suppressEvent){
17173         if(suppressEvent !== false){
17174             this.fireEvent("beforechildrenrendered", this);
17175         }
17176         var cs = this.childNodes;
17177         for(var i = 0, len = cs.length; i < len; i++){
17178             cs[i].render(true);
17179         }
17180         this.childrenRendered = true;
17181     },
17182
17183     // private
17184     sort : function(fn, scope){
17185         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17186         if(this.childrenRendered){
17187             var cs = this.childNodes;
17188             for(var i = 0, len = cs.length; i < len; i++){
17189                 cs[i].render(true);
17190             }
17191         }
17192     },
17193
17194     // private
17195     render : function(bulkRender){
17196         this.ui.render(bulkRender);
17197         if(!this.rendered){
17198             this.rendered = true;
17199             if(this.expanded){
17200                 this.expanded = false;
17201                 this.expand(false, false);
17202             }
17203         }
17204     },
17205
17206     // private
17207     renderIndent : function(deep, refresh){
17208         if(refresh){
17209             this.ui.childIndent = null;
17210         }
17211         this.ui.renderIndent();
17212         if(deep === true && this.childrenRendered){
17213             var cs = this.childNodes;
17214             for(var i = 0, len = cs.length; i < len; i++){
17215                 cs[i].renderIndent(true, refresh);
17216             }
17217         }
17218     }
17219 });/*
17220  * Based on:
17221  * Ext JS Library 1.1.1
17222  * Copyright(c) 2006-2007, Ext JS, LLC.
17223  *
17224  * Originally Released Under LGPL - original licence link has changed is not relivant.
17225  *
17226  * Fork - LGPL
17227  * <script type="text/javascript">
17228  */
17229  
17230 /**
17231  * @class Roo.tree.AsyncTreeNode
17232  * @extends Roo.tree.TreeNode
17233  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17234  * @constructor
17235  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17236  */
17237  Roo.tree.AsyncTreeNode = function(config){
17238     this.loaded = false;
17239     this.loading = false;
17240     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17241     /**
17242     * @event beforeload
17243     * Fires before this node is loaded, return false to cancel
17244     * @param {Node} this This node
17245     */
17246     this.addEvents({'beforeload':true, 'load': true});
17247     /**
17248     * @event load
17249     * Fires when this node is loaded
17250     * @param {Node} this This node
17251     */
17252     /**
17253      * The loader used by this node (defaults to using the tree's defined loader)
17254      * @type TreeLoader
17255      * @property loader
17256      */
17257 };
17258 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17259     expand : function(deep, anim, callback){
17260         if(this.loading){ // if an async load is already running, waiting til it's done
17261             var timer;
17262             var f = function(){
17263                 if(!this.loading){ // done loading
17264                     clearInterval(timer);
17265                     this.expand(deep, anim, callback);
17266                 }
17267             }.createDelegate(this);
17268             timer = setInterval(f, 200);
17269             return;
17270         }
17271         if(!this.loaded){
17272             if(this.fireEvent("beforeload", this) === false){
17273                 return;
17274             }
17275             this.loading = true;
17276             this.ui.beforeLoad(this);
17277             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17278             if(loader){
17279                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17280                 return;
17281             }
17282         }
17283         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17284     },
17285     
17286     /**
17287      * Returns true if this node is currently loading
17288      * @return {Boolean}
17289      */
17290     isLoading : function(){
17291         return this.loading;  
17292     },
17293     
17294     loadComplete : function(deep, anim, callback){
17295         this.loading = false;
17296         this.loaded = true;
17297         this.ui.afterLoad(this);
17298         this.fireEvent("load", this);
17299         this.expand(deep, anim, callback);
17300     },
17301     
17302     /**
17303      * Returns true if this node has been loaded
17304      * @return {Boolean}
17305      */
17306     isLoaded : function(){
17307         return this.loaded;
17308     },
17309     
17310     hasChildNodes : function(){
17311         if(!this.isLeaf() && !this.loaded){
17312             return true;
17313         }else{
17314             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17315         }
17316     },
17317
17318     /**
17319      * Trigger a reload for this node
17320      * @param {Function} callback
17321      */
17322     reload : function(callback){
17323         this.collapse(false, false);
17324         while(this.firstChild){
17325             this.removeChild(this.firstChild);
17326         }
17327         this.childrenRendered = false;
17328         this.loaded = false;
17329         if(this.isHiddenRoot()){
17330             this.expanded = false;
17331         }
17332         this.expand(false, false, callback);
17333     }
17334 });/*
17335  * Based on:
17336  * Ext JS Library 1.1.1
17337  * Copyright(c) 2006-2007, Ext JS, LLC.
17338  *
17339  * Originally Released Under LGPL - original licence link has changed is not relivant.
17340  *
17341  * Fork - LGPL
17342  * <script type="text/javascript">
17343  */
17344  
17345 /**
17346  * @class Roo.tree.TreeNodeUI
17347  * @constructor
17348  * @param {Object} node The node to render
17349  * The TreeNode UI implementation is separate from the
17350  * tree implementation. Unless you are customizing the tree UI,
17351  * you should never have to use this directly.
17352  */
17353 Roo.tree.TreeNodeUI = function(node){
17354     this.node = node;
17355     this.rendered = false;
17356     this.animating = false;
17357     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17358 };
17359
17360 Roo.tree.TreeNodeUI.prototype = {
17361     removeChild : function(node){
17362         if(this.rendered){
17363             this.ctNode.removeChild(node.ui.getEl());
17364         }
17365     },
17366
17367     beforeLoad : function(){
17368          this.addClass("x-tree-node-loading");
17369     },
17370
17371     afterLoad : function(){
17372          this.removeClass("x-tree-node-loading");
17373     },
17374
17375     onTextChange : function(node, text, oldText){
17376         if(this.rendered){
17377             this.textNode.innerHTML = text;
17378         }
17379     },
17380
17381     onDisableChange : function(node, state){
17382         this.disabled = state;
17383         if(state){
17384             this.addClass("x-tree-node-disabled");
17385         }else{
17386             this.removeClass("x-tree-node-disabled");
17387         }
17388     },
17389
17390     onSelectedChange : function(state){
17391         if(state){
17392             this.focus();
17393             this.addClass("x-tree-selected");
17394         }else{
17395             //this.blur();
17396             this.removeClass("x-tree-selected");
17397         }
17398     },
17399
17400     onMove : function(tree, node, oldParent, newParent, index, refNode){
17401         this.childIndent = null;
17402         if(this.rendered){
17403             var targetNode = newParent.ui.getContainer();
17404             if(!targetNode){//target not rendered
17405                 this.holder = document.createElement("div");
17406                 this.holder.appendChild(this.wrap);
17407                 return;
17408             }
17409             var insertBefore = refNode ? refNode.ui.getEl() : null;
17410             if(insertBefore){
17411                 targetNode.insertBefore(this.wrap, insertBefore);
17412             }else{
17413                 targetNode.appendChild(this.wrap);
17414             }
17415             this.node.renderIndent(true);
17416         }
17417     },
17418
17419     addClass : function(cls){
17420         if(this.elNode){
17421             Roo.fly(this.elNode).addClass(cls);
17422         }
17423     },
17424
17425     removeClass : function(cls){
17426         if(this.elNode){
17427             Roo.fly(this.elNode).removeClass(cls);
17428         }
17429     },
17430
17431     remove : function(){
17432         if(this.rendered){
17433             this.holder = document.createElement("div");
17434             this.holder.appendChild(this.wrap);
17435         }
17436     },
17437
17438     fireEvent : function(){
17439         return this.node.fireEvent.apply(this.node, arguments);
17440     },
17441
17442     initEvents : function(){
17443         this.node.on("move", this.onMove, this);
17444         var E = Roo.EventManager;
17445         var a = this.anchor;
17446
17447         var el = Roo.fly(a, '_treeui');
17448
17449         if(Roo.isOpera){ // opera render bug ignores the CSS
17450             el.setStyle("text-decoration", "none");
17451         }
17452
17453         el.on("click", this.onClick, this);
17454         el.on("dblclick", this.onDblClick, this);
17455
17456         if(this.checkbox){
17457             Roo.EventManager.on(this.checkbox,
17458                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17459         }
17460
17461         el.on("contextmenu", this.onContextMenu, this);
17462
17463         var icon = Roo.fly(this.iconNode);
17464         icon.on("click", this.onClick, this);
17465         icon.on("dblclick", this.onDblClick, this);
17466         icon.on("contextmenu", this.onContextMenu, this);
17467         E.on(this.ecNode, "click", this.ecClick, this, true);
17468
17469         if(this.node.disabled){
17470             this.addClass("x-tree-node-disabled");
17471         }
17472         if(this.node.hidden){
17473             this.addClass("x-tree-node-disabled");
17474         }
17475         var ot = this.node.getOwnerTree();
17476         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17477         if(dd && (!this.node.isRoot || ot.rootVisible)){
17478             Roo.dd.Registry.register(this.elNode, {
17479                 node: this.node,
17480                 handles: this.getDDHandles(),
17481                 isHandle: false
17482             });
17483         }
17484     },
17485
17486     getDDHandles : function(){
17487         return [this.iconNode, this.textNode];
17488     },
17489
17490     hide : function(){
17491         if(this.rendered){
17492             this.wrap.style.display = "none";
17493         }
17494     },
17495
17496     show : function(){
17497         if(this.rendered){
17498             this.wrap.style.display = "";
17499         }
17500     },
17501
17502     onContextMenu : function(e){
17503         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17504             e.preventDefault();
17505             this.focus();
17506             this.fireEvent("contextmenu", this.node, e);
17507         }
17508     },
17509
17510     onClick : function(e){
17511         if(this.dropping){
17512             e.stopEvent();
17513             return;
17514         }
17515         if(this.fireEvent("beforeclick", this.node, e) !== false){
17516             if(!this.disabled && this.node.attributes.href){
17517                 this.fireEvent("click", this.node, e);
17518                 return;
17519             }
17520             e.preventDefault();
17521             if(this.disabled){
17522                 return;
17523             }
17524
17525             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17526                 this.node.toggle();
17527             }
17528
17529             this.fireEvent("click", this.node, e);
17530         }else{
17531             e.stopEvent();
17532         }
17533     },
17534
17535     onDblClick : function(e){
17536         e.preventDefault();
17537         if(this.disabled){
17538             return;
17539         }
17540         if(this.checkbox){
17541             this.toggleCheck();
17542         }
17543         if(!this.animating && this.node.hasChildNodes()){
17544             this.node.toggle();
17545         }
17546         this.fireEvent("dblclick", this.node, e);
17547     },
17548
17549     onCheckChange : function(){
17550         var checked = this.checkbox.checked;
17551         this.node.attributes.checked = checked;
17552         this.fireEvent('checkchange', this.node, checked);
17553     },
17554
17555     ecClick : function(e){
17556         if(!this.animating && this.node.hasChildNodes()){
17557             this.node.toggle();
17558         }
17559     },
17560
17561     startDrop : function(){
17562         this.dropping = true;
17563     },
17564
17565     // delayed drop so the click event doesn't get fired on a drop
17566     endDrop : function(){
17567        setTimeout(function(){
17568            this.dropping = false;
17569        }.createDelegate(this), 50);
17570     },
17571
17572     expand : function(){
17573         this.updateExpandIcon();
17574         this.ctNode.style.display = "";
17575     },
17576
17577     focus : function(){
17578         if(!this.node.preventHScroll){
17579             try{this.anchor.focus();
17580             }catch(e){}
17581         }else if(!Roo.isIE){
17582             try{
17583                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17584                 var l = noscroll.scrollLeft;
17585                 this.anchor.focus();
17586                 noscroll.scrollLeft = l;
17587             }catch(e){}
17588         }
17589     },
17590
17591     toggleCheck : function(value){
17592         var cb = this.checkbox;
17593         if(cb){
17594             cb.checked = (value === undefined ? !cb.checked : value);
17595         }
17596     },
17597
17598     blur : function(){
17599         try{
17600             this.anchor.blur();
17601         }catch(e){}
17602     },
17603
17604     animExpand : function(callback){
17605         var ct = Roo.get(this.ctNode);
17606         ct.stopFx();
17607         if(!this.node.hasChildNodes()){
17608             this.updateExpandIcon();
17609             this.ctNode.style.display = "";
17610             Roo.callback(callback);
17611             return;
17612         }
17613         this.animating = true;
17614         this.updateExpandIcon();
17615
17616         ct.slideIn('t', {
17617            callback : function(){
17618                this.animating = false;
17619                Roo.callback(callback);
17620             },
17621             scope: this,
17622             duration: this.node.ownerTree.duration || .25
17623         });
17624     },
17625
17626     highlight : function(){
17627         var tree = this.node.getOwnerTree();
17628         Roo.fly(this.wrap).highlight(
17629             tree.hlColor || "C3DAF9",
17630             {endColor: tree.hlBaseColor}
17631         );
17632     },
17633
17634     collapse : function(){
17635         this.updateExpandIcon();
17636         this.ctNode.style.display = "none";
17637     },
17638
17639     animCollapse : function(callback){
17640         var ct = Roo.get(this.ctNode);
17641         ct.enableDisplayMode('block');
17642         ct.stopFx();
17643
17644         this.animating = true;
17645         this.updateExpandIcon();
17646
17647         ct.slideOut('t', {
17648             callback : function(){
17649                this.animating = false;
17650                Roo.callback(callback);
17651             },
17652             scope: this,
17653             duration: this.node.ownerTree.duration || .25
17654         });
17655     },
17656
17657     getContainer : function(){
17658         return this.ctNode;
17659     },
17660
17661     getEl : function(){
17662         return this.wrap;
17663     },
17664
17665     appendDDGhost : function(ghostNode){
17666         ghostNode.appendChild(this.elNode.cloneNode(true));
17667     },
17668
17669     getDDRepairXY : function(){
17670         return Roo.lib.Dom.getXY(this.iconNode);
17671     },
17672
17673     onRender : function(){
17674         this.render();
17675     },
17676
17677     render : function(bulkRender){
17678         var n = this.node, a = n.attributes;
17679         var targetNode = n.parentNode ?
17680               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
17681
17682         if(!this.rendered){
17683             this.rendered = true;
17684
17685             this.renderElements(n, a, targetNode, bulkRender);
17686
17687             if(a.qtip){
17688                if(this.textNode.setAttributeNS){
17689                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
17690                    if(a.qtipTitle){
17691                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
17692                    }
17693                }else{
17694                    this.textNode.setAttribute("ext:qtip", a.qtip);
17695                    if(a.qtipTitle){
17696                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
17697                    }
17698                }
17699             }else if(a.qtipCfg){
17700                 a.qtipCfg.target = Roo.id(this.textNode);
17701                 Roo.QuickTips.register(a.qtipCfg);
17702             }
17703             this.initEvents();
17704             if(!this.node.expanded){
17705                 this.updateExpandIcon();
17706             }
17707         }else{
17708             if(bulkRender === true) {
17709                 targetNode.appendChild(this.wrap);
17710             }
17711         }
17712     },
17713
17714     renderElements : function(n, a, targetNode, bulkRender)
17715     {
17716         // add some indent caching, this helps performance when rendering a large tree
17717         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
17718         var t = n.getOwnerTree();
17719         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
17720         if (typeof(n.attributes.html) != 'undefined') {
17721             txt = n.attributes.html;
17722         }
17723         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
17724         var cb = typeof a.checked == 'boolean';
17725         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
17726         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
17727             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
17728             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
17729             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
17730             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
17731             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
17732              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
17733                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
17734             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
17735             "</li>"];
17736
17737         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
17738             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
17739                                 n.nextSibling.ui.getEl(), buf.join(""));
17740         }else{
17741             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
17742         }
17743
17744         this.elNode = this.wrap.childNodes[0];
17745         this.ctNode = this.wrap.childNodes[1];
17746         var cs = this.elNode.childNodes;
17747         this.indentNode = cs[0];
17748         this.ecNode = cs[1];
17749         this.iconNode = cs[2];
17750         var index = 3;
17751         if(cb){
17752             this.checkbox = cs[3];
17753             index++;
17754         }
17755         this.anchor = cs[index];
17756         this.textNode = cs[index].firstChild;
17757     },
17758
17759     getAnchor : function(){
17760         return this.anchor;
17761     },
17762
17763     getTextEl : function(){
17764         return this.textNode;
17765     },
17766
17767     getIconEl : function(){
17768         return this.iconNode;
17769     },
17770
17771     isChecked : function(){
17772         return this.checkbox ? this.checkbox.checked : false;
17773     },
17774
17775     updateExpandIcon : function(){
17776         if(this.rendered){
17777             var n = this.node, c1, c2;
17778             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
17779             var hasChild = n.hasChildNodes();
17780             if(hasChild){
17781                 if(n.expanded){
17782                     cls += "-minus";
17783                     c1 = "x-tree-node-collapsed";
17784                     c2 = "x-tree-node-expanded";
17785                 }else{
17786                     cls += "-plus";
17787                     c1 = "x-tree-node-expanded";
17788                     c2 = "x-tree-node-collapsed";
17789                 }
17790                 if(this.wasLeaf){
17791                     this.removeClass("x-tree-node-leaf");
17792                     this.wasLeaf = false;
17793                 }
17794                 if(this.c1 != c1 || this.c2 != c2){
17795                     Roo.fly(this.elNode).replaceClass(c1, c2);
17796                     this.c1 = c1; this.c2 = c2;
17797                 }
17798             }else{
17799                 // this changes non-leafs into leafs if they have no children.
17800                 // it's not very rational behaviour..
17801                 
17802                 if(!this.wasLeaf && this.node.leaf){
17803                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
17804                     delete this.c1;
17805                     delete this.c2;
17806                     this.wasLeaf = true;
17807                 }
17808             }
17809             var ecc = "x-tree-ec-icon "+cls;
17810             if(this.ecc != ecc){
17811                 this.ecNode.className = ecc;
17812                 this.ecc = ecc;
17813             }
17814         }
17815     },
17816
17817     getChildIndent : function(){
17818         if(!this.childIndent){
17819             var buf = [];
17820             var p = this.node;
17821             while(p){
17822                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
17823                     if(!p.isLast()) {
17824                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
17825                     } else {
17826                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
17827                     }
17828                 }
17829                 p = p.parentNode;
17830             }
17831             this.childIndent = buf.join("");
17832         }
17833         return this.childIndent;
17834     },
17835
17836     renderIndent : function(){
17837         if(this.rendered){
17838             var indent = "";
17839             var p = this.node.parentNode;
17840             if(p){
17841                 indent = p.ui.getChildIndent();
17842             }
17843             if(this.indentMarkup != indent){ // don't rerender if not required
17844                 this.indentNode.innerHTML = indent;
17845                 this.indentMarkup = indent;
17846             }
17847             this.updateExpandIcon();
17848         }
17849     }
17850 };
17851
17852 Roo.tree.RootTreeNodeUI = function(){
17853     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
17854 };
17855 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
17856     render : function(){
17857         if(!this.rendered){
17858             var targetNode = this.node.ownerTree.innerCt.dom;
17859             this.node.expanded = true;
17860             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
17861             this.wrap = this.ctNode = targetNode.firstChild;
17862         }
17863     },
17864     collapse : function(){
17865     },
17866     expand : function(){
17867     }
17868 });/*
17869  * Based on:
17870  * Ext JS Library 1.1.1
17871  * Copyright(c) 2006-2007, Ext JS, LLC.
17872  *
17873  * Originally Released Under LGPL - original licence link has changed is not relivant.
17874  *
17875  * Fork - LGPL
17876  * <script type="text/javascript">
17877  */
17878 /**
17879  * @class Roo.tree.TreeLoader
17880  * @extends Roo.util.Observable
17881  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
17882  * nodes from a specified URL. The response must be a javascript Array definition
17883  * who's elements are node definition objects. eg:
17884  * <pre><code>
17885 {  success : true,
17886    data :      [
17887    
17888     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
17889     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
17890     ]
17891 }
17892
17893
17894 </code></pre>
17895  * <br><br>
17896  * The old style respose with just an array is still supported, but not recommended.
17897  * <br><br>
17898  *
17899  * A server request is sent, and child nodes are loaded only when a node is expanded.
17900  * The loading node's id is passed to the server under the parameter name "node" to
17901  * enable the server to produce the correct child nodes.
17902  * <br><br>
17903  * To pass extra parameters, an event handler may be attached to the "beforeload"
17904  * event, and the parameters specified in the TreeLoader's baseParams property:
17905  * <pre><code>
17906     myTreeLoader.on("beforeload", function(treeLoader, node) {
17907         this.baseParams.category = node.attributes.category;
17908     }, this);
17909 </code></pre><
17910  * This would pass an HTTP parameter called "category" to the server containing
17911  * the value of the Node's "category" attribute.
17912  * @constructor
17913  * Creates a new Treeloader.
17914  * @param {Object} config A config object containing config properties.
17915  */
17916 Roo.tree.TreeLoader = function(config){
17917     this.baseParams = {};
17918     this.requestMethod = "POST";
17919     Roo.apply(this, config);
17920
17921     this.addEvents({
17922     
17923         /**
17924          * @event beforeload
17925          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
17926          * @param {Object} This TreeLoader object.
17927          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17928          * @param {Object} callback The callback function specified in the {@link #load} call.
17929          */
17930         beforeload : true,
17931         /**
17932          * @event load
17933          * Fires when the node has been successfuly loaded.
17934          * @param {Object} This TreeLoader object.
17935          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17936          * @param {Object} response The response object containing the data from the server.
17937          */
17938         load : true,
17939         /**
17940          * @event loadexception
17941          * Fires if the network request failed.
17942          * @param {Object} This TreeLoader object.
17943          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17944          * @param {Object} response The response object containing the data from the server.
17945          */
17946         loadexception : true,
17947         /**
17948          * @event create
17949          * Fires before a node is created, enabling you to return custom Node types 
17950          * @param {Object} This TreeLoader object.
17951          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
17952          */
17953         create : true
17954     });
17955
17956     Roo.tree.TreeLoader.superclass.constructor.call(this);
17957 };
17958
17959 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
17960     /**
17961     * @cfg {String} dataUrl The URL from which to request a Json string which
17962     * specifies an array of node definition object representing the child nodes
17963     * to be loaded.
17964     */
17965     /**
17966     * @cfg {String} requestMethod either GET or POST
17967     * defaults to POST (due to BC)
17968     * to be loaded.
17969     */
17970     /**
17971     * @cfg {Object} baseParams (optional) An object containing properties which
17972     * specify HTTP parameters to be passed to each request for child nodes.
17973     */
17974     /**
17975     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
17976     * created by this loader. If the attributes sent by the server have an attribute in this object,
17977     * they take priority.
17978     */
17979     /**
17980     * @cfg {Object} uiProviders (optional) An object containing properties which
17981     * 
17982     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
17983     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
17984     * <i>uiProvider</i> attribute of a returned child node is a string rather
17985     * than a reference to a TreeNodeUI implementation, this that string value
17986     * is used as a property name in the uiProviders object. You can define the provider named
17987     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
17988     */
17989     uiProviders : {},
17990
17991     /**
17992     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
17993     * child nodes before loading.
17994     */
17995     clearOnLoad : true,
17996
17997     /**
17998     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
17999     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18000     * Grid query { data : [ .....] }
18001     */
18002     
18003     root : false,
18004      /**
18005     * @cfg {String} queryParam (optional) 
18006     * Name of the query as it will be passed on the querystring (defaults to 'node')
18007     * eg. the request will be ?node=[id]
18008     */
18009     
18010     
18011     queryParam: false,
18012     
18013     /**
18014      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18015      * This is called automatically when a node is expanded, but may be used to reload
18016      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18017      * @param {Roo.tree.TreeNode} node
18018      * @param {Function} callback
18019      */
18020     load : function(node, callback){
18021         if(this.clearOnLoad){
18022             while(node.firstChild){
18023                 node.removeChild(node.firstChild);
18024             }
18025         }
18026         if(node.attributes.children){ // preloaded json children
18027             var cs = node.attributes.children;
18028             for(var i = 0, len = cs.length; i < len; i++){
18029                 node.appendChild(this.createNode(cs[i]));
18030             }
18031             if(typeof callback == "function"){
18032                 callback();
18033             }
18034         }else if(this.dataUrl){
18035             this.requestData(node, callback);
18036         }
18037     },
18038
18039     getParams: function(node){
18040         var buf = [], bp = this.baseParams;
18041         for(var key in bp){
18042             if(typeof bp[key] != "function"){
18043                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18044             }
18045         }
18046         var n = this.queryParam === false ? 'node' : this.queryParam;
18047         buf.push(n + "=", encodeURIComponent(node.id));
18048         return buf.join("");
18049     },
18050
18051     requestData : function(node, callback){
18052         if(this.fireEvent("beforeload", this, node, callback) !== false){
18053             this.transId = Roo.Ajax.request({
18054                 method:this.requestMethod,
18055                 url: this.dataUrl||this.url,
18056                 success: this.handleResponse,
18057                 failure: this.handleFailure,
18058                 scope: this,
18059                 argument: {callback: callback, node: node},
18060                 params: this.getParams(node)
18061             });
18062         }else{
18063             // if the load is cancelled, make sure we notify
18064             // the node that we are done
18065             if(typeof callback == "function"){
18066                 callback();
18067             }
18068         }
18069     },
18070
18071     isLoading : function(){
18072         return this.transId ? true : false;
18073     },
18074
18075     abort : function(){
18076         if(this.isLoading()){
18077             Roo.Ajax.abort(this.transId);
18078         }
18079     },
18080
18081     // private
18082     createNode : function(attr)
18083     {
18084         // apply baseAttrs, nice idea Corey!
18085         if(this.baseAttrs){
18086             Roo.applyIf(attr, this.baseAttrs);
18087         }
18088         if(this.applyLoader !== false){
18089             attr.loader = this;
18090         }
18091         // uiProvider = depreciated..
18092         
18093         if(typeof(attr.uiProvider) == 'string'){
18094            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18095                 /**  eval:var:attr */ eval(attr.uiProvider);
18096         }
18097         if(typeof(this.uiProviders['default']) != 'undefined') {
18098             attr.uiProvider = this.uiProviders['default'];
18099         }
18100         
18101         this.fireEvent('create', this, attr);
18102         
18103         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18104         return(attr.leaf ?
18105                         new Roo.tree.TreeNode(attr) :
18106                         new Roo.tree.AsyncTreeNode(attr));
18107     },
18108
18109     processResponse : function(response, node, callback)
18110     {
18111         var json = response.responseText;
18112         try {
18113             
18114             var o = Roo.decode(json);
18115             
18116             if (this.root === false && typeof(o.success) != undefined) {
18117                 this.root = 'data'; // the default behaviour for list like data..
18118                 }
18119                 
18120             if (this.root !== false &&  !o.success) {
18121                 // it's a failure condition.
18122                 var a = response.argument;
18123                 this.fireEvent("loadexception", this, a.node, response);
18124                 Roo.log("Load failed - should have a handler really");
18125                 return;
18126             }
18127             
18128             
18129             
18130             if (this.root !== false) {
18131                  o = o[this.root];
18132             }
18133             
18134             for(var i = 0, len = o.length; i < len; i++){
18135                 var n = this.createNode(o[i]);
18136                 if(n){
18137                     node.appendChild(n);
18138                 }
18139             }
18140             if(typeof callback == "function"){
18141                 callback(this, node);
18142             }
18143         }catch(e){
18144             this.handleFailure(response);
18145         }
18146     },
18147
18148     handleResponse : function(response){
18149         this.transId = false;
18150         var a = response.argument;
18151         this.processResponse(response, a.node, a.callback);
18152         this.fireEvent("load", this, a.node, response);
18153     },
18154
18155     handleFailure : function(response)
18156     {
18157         // should handle failure better..
18158         this.transId = false;
18159         var a = response.argument;
18160         this.fireEvent("loadexception", this, a.node, response);
18161         if(typeof a.callback == "function"){
18162             a.callback(this, a.node);
18163         }
18164     }
18165 });/*
18166  * Based on:
18167  * Ext JS Library 1.1.1
18168  * Copyright(c) 2006-2007, Ext JS, LLC.
18169  *
18170  * Originally Released Under LGPL - original licence link has changed is not relivant.
18171  *
18172  * Fork - LGPL
18173  * <script type="text/javascript">
18174  */
18175
18176 /**
18177 * @class Roo.tree.TreeFilter
18178 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18179 * @param {TreePanel} tree
18180 * @param {Object} config (optional)
18181  */
18182 Roo.tree.TreeFilter = function(tree, config){
18183     this.tree = tree;
18184     this.filtered = {};
18185     Roo.apply(this, config);
18186 };
18187
18188 Roo.tree.TreeFilter.prototype = {
18189     clearBlank:false,
18190     reverse:false,
18191     autoClear:false,
18192     remove:false,
18193
18194      /**
18195      * Filter the data by a specific attribute.
18196      * @param {String/RegExp} value Either string that the attribute value
18197      * should start with or a RegExp to test against the attribute
18198      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18199      * @param {TreeNode} startNode (optional) The node to start the filter at.
18200      */
18201     filter : function(value, attr, startNode){
18202         attr = attr || "text";
18203         var f;
18204         if(typeof value == "string"){
18205             var vlen = value.length;
18206             // auto clear empty filter
18207             if(vlen == 0 && this.clearBlank){
18208                 this.clear();
18209                 return;
18210             }
18211             value = value.toLowerCase();
18212             f = function(n){
18213                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18214             };
18215         }else if(value.exec){ // regex?
18216             f = function(n){
18217                 return value.test(n.attributes[attr]);
18218             };
18219         }else{
18220             throw 'Illegal filter type, must be string or regex';
18221         }
18222         this.filterBy(f, null, startNode);
18223         },
18224
18225     /**
18226      * Filter by a function. The passed function will be called with each
18227      * node in the tree (or from the startNode). If the function returns true, the node is kept
18228      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18229      * @param {Function} fn The filter function
18230      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18231      */
18232     filterBy : function(fn, scope, startNode){
18233         startNode = startNode || this.tree.root;
18234         if(this.autoClear){
18235             this.clear();
18236         }
18237         var af = this.filtered, rv = this.reverse;
18238         var f = function(n){
18239             if(n == startNode){
18240                 return true;
18241             }
18242             if(af[n.id]){
18243                 return false;
18244             }
18245             var m = fn.call(scope || n, n);
18246             if(!m || rv){
18247                 af[n.id] = n;
18248                 n.ui.hide();
18249                 return false;
18250             }
18251             return true;
18252         };
18253         startNode.cascade(f);
18254         if(this.remove){
18255            for(var id in af){
18256                if(typeof id != "function"){
18257                    var n = af[id];
18258                    if(n && n.parentNode){
18259                        n.parentNode.removeChild(n);
18260                    }
18261                }
18262            }
18263         }
18264     },
18265
18266     /**
18267      * Clears the current filter. Note: with the "remove" option
18268      * set a filter cannot be cleared.
18269      */
18270     clear : function(){
18271         var t = this.tree;
18272         var af = this.filtered;
18273         for(var id in af){
18274             if(typeof id != "function"){
18275                 var n = af[id];
18276                 if(n){
18277                     n.ui.show();
18278                 }
18279             }
18280         }
18281         this.filtered = {};
18282     }
18283 };
18284 /*
18285  * Based on:
18286  * Ext JS Library 1.1.1
18287  * Copyright(c) 2006-2007, Ext JS, LLC.
18288  *
18289  * Originally Released Under LGPL - original licence link has changed is not relivant.
18290  *
18291  * Fork - LGPL
18292  * <script type="text/javascript">
18293  */
18294  
18295
18296 /**
18297  * @class Roo.tree.TreeSorter
18298  * Provides sorting of nodes in a TreePanel
18299  * 
18300  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18301  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18302  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18303  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18304  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18305  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18306  * @constructor
18307  * @param {TreePanel} tree
18308  * @param {Object} config
18309  */
18310 Roo.tree.TreeSorter = function(tree, config){
18311     Roo.apply(this, config);
18312     tree.on("beforechildrenrendered", this.doSort, this);
18313     tree.on("append", this.updateSort, this);
18314     tree.on("insert", this.updateSort, this);
18315     
18316     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18317     var p = this.property || "text";
18318     var sortType = this.sortType;
18319     var fs = this.folderSort;
18320     var cs = this.caseSensitive === true;
18321     var leafAttr = this.leafAttr || 'leaf';
18322
18323     this.sortFn = function(n1, n2){
18324         if(fs){
18325             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18326                 return 1;
18327             }
18328             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18329                 return -1;
18330             }
18331         }
18332         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18333         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18334         if(v1 < v2){
18335                         return dsc ? +1 : -1;
18336                 }else if(v1 > v2){
18337                         return dsc ? -1 : +1;
18338         }else{
18339                 return 0;
18340         }
18341     };
18342 };
18343
18344 Roo.tree.TreeSorter.prototype = {
18345     doSort : function(node){
18346         node.sort(this.sortFn);
18347     },
18348     
18349     compareNodes : function(n1, n2){
18350         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18351     },
18352     
18353     updateSort : function(tree, node){
18354         if(node.childrenRendered){
18355             this.doSort.defer(1, this, [node]);
18356         }
18357     }
18358 };/*
18359  * Based on:
18360  * Ext JS Library 1.1.1
18361  * Copyright(c) 2006-2007, Ext JS, LLC.
18362  *
18363  * Originally Released Under LGPL - original licence link has changed is not relivant.
18364  *
18365  * Fork - LGPL
18366  * <script type="text/javascript">
18367  */
18368
18369 if(Roo.dd.DropZone){
18370     
18371 Roo.tree.TreeDropZone = function(tree, config){
18372     this.allowParentInsert = false;
18373     this.allowContainerDrop = false;
18374     this.appendOnly = false;
18375     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18376     this.tree = tree;
18377     this.lastInsertClass = "x-tree-no-status";
18378     this.dragOverData = {};
18379 };
18380
18381 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18382     ddGroup : "TreeDD",
18383     scroll:  true,
18384     
18385     expandDelay : 1000,
18386     
18387     expandNode : function(node){
18388         if(node.hasChildNodes() && !node.isExpanded()){
18389             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18390         }
18391     },
18392     
18393     queueExpand : function(node){
18394         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18395     },
18396     
18397     cancelExpand : function(){
18398         if(this.expandProcId){
18399             clearTimeout(this.expandProcId);
18400             this.expandProcId = false;
18401         }
18402     },
18403     
18404     isValidDropPoint : function(n, pt, dd, e, data){
18405         if(!n || !data){ return false; }
18406         var targetNode = n.node;
18407         var dropNode = data.node;
18408         // default drop rules
18409         if(!(targetNode && targetNode.isTarget && pt)){
18410             return false;
18411         }
18412         if(pt == "append" && targetNode.allowChildren === false){
18413             return false;
18414         }
18415         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18416             return false;
18417         }
18418         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18419             return false;
18420         }
18421         // reuse the object
18422         var overEvent = this.dragOverData;
18423         overEvent.tree = this.tree;
18424         overEvent.target = targetNode;
18425         overEvent.data = data;
18426         overEvent.point = pt;
18427         overEvent.source = dd;
18428         overEvent.rawEvent = e;
18429         overEvent.dropNode = dropNode;
18430         overEvent.cancel = false;  
18431         var result = this.tree.fireEvent("nodedragover", overEvent);
18432         return overEvent.cancel === false && result !== false;
18433     },
18434     
18435     getDropPoint : function(e, n, dd)
18436     {
18437         var tn = n.node;
18438         if(tn.isRoot){
18439             return tn.allowChildren !== false ? "append" : false; // always append for root
18440         }
18441         var dragEl = n.ddel;
18442         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18443         var y = Roo.lib.Event.getPageY(e);
18444         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18445         
18446         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18447         var noAppend = tn.allowChildren === false;
18448         if(this.appendOnly || tn.parentNode.allowChildren === false){
18449             return noAppend ? false : "append";
18450         }
18451         var noBelow = false;
18452         if(!this.allowParentInsert){
18453             noBelow = tn.hasChildNodes() && tn.isExpanded();
18454         }
18455         var q = (b - t) / (noAppend ? 2 : 3);
18456         if(y >= t && y < (t + q)){
18457             return "above";
18458         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18459             return "below";
18460         }else{
18461             return "append";
18462         }
18463     },
18464     
18465     onNodeEnter : function(n, dd, e, data)
18466     {
18467         this.cancelExpand();
18468     },
18469     
18470     onNodeOver : function(n, dd, e, data)
18471     {
18472        
18473         var pt = this.getDropPoint(e, n, dd);
18474         var node = n.node;
18475         
18476         // auto node expand check
18477         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18478             this.queueExpand(node);
18479         }else if(pt != "append"){
18480             this.cancelExpand();
18481         }
18482         
18483         // set the insert point style on the target node
18484         var returnCls = this.dropNotAllowed;
18485         if(this.isValidDropPoint(n, pt, dd, e, data)){
18486            if(pt){
18487                var el = n.ddel;
18488                var cls;
18489                if(pt == "above"){
18490                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18491                    cls = "x-tree-drag-insert-above";
18492                }else if(pt == "below"){
18493                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18494                    cls = "x-tree-drag-insert-below";
18495                }else{
18496                    returnCls = "x-tree-drop-ok-append";
18497                    cls = "x-tree-drag-append";
18498                }
18499                if(this.lastInsertClass != cls){
18500                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18501                    this.lastInsertClass = cls;
18502                }
18503            }
18504        }
18505        return returnCls;
18506     },
18507     
18508     onNodeOut : function(n, dd, e, data){
18509         
18510         this.cancelExpand();
18511         this.removeDropIndicators(n);
18512     },
18513     
18514     onNodeDrop : function(n, dd, e, data){
18515         var point = this.getDropPoint(e, n, dd);
18516         var targetNode = n.node;
18517         targetNode.ui.startDrop();
18518         if(!this.isValidDropPoint(n, point, dd, e, data)){
18519             targetNode.ui.endDrop();
18520             return false;
18521         }
18522         // first try to find the drop node
18523         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18524         var dropEvent = {
18525             tree : this.tree,
18526             target: targetNode,
18527             data: data,
18528             point: point,
18529             source: dd,
18530             rawEvent: e,
18531             dropNode: dropNode,
18532             cancel: !dropNode   
18533         };
18534         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18535         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18536             targetNode.ui.endDrop();
18537             return false;
18538         }
18539         // allow target changing
18540         targetNode = dropEvent.target;
18541         if(point == "append" && !targetNode.isExpanded()){
18542             targetNode.expand(false, null, function(){
18543                 this.completeDrop(dropEvent);
18544             }.createDelegate(this));
18545         }else{
18546             this.completeDrop(dropEvent);
18547         }
18548         return true;
18549     },
18550     
18551     completeDrop : function(de){
18552         var ns = de.dropNode, p = de.point, t = de.target;
18553         if(!(ns instanceof Array)){
18554             ns = [ns];
18555         }
18556         var n;
18557         for(var i = 0, len = ns.length; i < len; i++){
18558             n = ns[i];
18559             if(p == "above"){
18560                 t.parentNode.insertBefore(n, t);
18561             }else if(p == "below"){
18562                 t.parentNode.insertBefore(n, t.nextSibling);
18563             }else{
18564                 t.appendChild(n);
18565             }
18566         }
18567         n.ui.focus();
18568         if(this.tree.hlDrop){
18569             n.ui.highlight();
18570         }
18571         t.ui.endDrop();
18572         this.tree.fireEvent("nodedrop", de);
18573     },
18574     
18575     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18576         if(this.tree.hlDrop){
18577             dropNode.ui.focus();
18578             dropNode.ui.highlight();
18579         }
18580         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18581     },
18582     
18583     getTree : function(){
18584         return this.tree;
18585     },
18586     
18587     removeDropIndicators : function(n){
18588         if(n && n.ddel){
18589             var el = n.ddel;
18590             Roo.fly(el).removeClass([
18591                     "x-tree-drag-insert-above",
18592                     "x-tree-drag-insert-below",
18593                     "x-tree-drag-append"]);
18594             this.lastInsertClass = "_noclass";
18595         }
18596     },
18597     
18598     beforeDragDrop : function(target, e, id){
18599         this.cancelExpand();
18600         return true;
18601     },
18602     
18603     afterRepair : function(data){
18604         if(data && Roo.enableFx){
18605             data.node.ui.highlight();
18606         }
18607         this.hideProxy();
18608     } 
18609     
18610 });
18611
18612 }
18613 /*
18614  * Based on:
18615  * Ext JS Library 1.1.1
18616  * Copyright(c) 2006-2007, Ext JS, LLC.
18617  *
18618  * Originally Released Under LGPL - original licence link has changed is not relivant.
18619  *
18620  * Fork - LGPL
18621  * <script type="text/javascript">
18622  */
18623  
18624
18625 if(Roo.dd.DragZone){
18626 Roo.tree.TreeDragZone = function(tree, config){
18627     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18628     this.tree = tree;
18629 };
18630
18631 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18632     ddGroup : "TreeDD",
18633    
18634     onBeforeDrag : function(data, e){
18635         var n = data.node;
18636         return n && n.draggable && !n.disabled;
18637     },
18638      
18639     
18640     onInitDrag : function(e){
18641         var data = this.dragData;
18642         this.tree.getSelectionModel().select(data.node);
18643         this.proxy.update("");
18644         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18645         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18646     },
18647     
18648     getRepairXY : function(e, data){
18649         return data.node.ui.getDDRepairXY();
18650     },
18651     
18652     onEndDrag : function(data, e){
18653         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18654         
18655         
18656     },
18657     
18658     onValidDrop : function(dd, e, id){
18659         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18660         this.hideProxy();
18661     },
18662     
18663     beforeInvalidDrop : function(e, id){
18664         // this scrolls the original position back into view
18665         var sm = this.tree.getSelectionModel();
18666         sm.clearSelections();
18667         sm.select(this.dragData.node);
18668     }
18669 });
18670 }/*
18671  * Based on:
18672  * Ext JS Library 1.1.1
18673  * Copyright(c) 2006-2007, Ext JS, LLC.
18674  *
18675  * Originally Released Under LGPL - original licence link has changed is not relivant.
18676  *
18677  * Fork - LGPL
18678  * <script type="text/javascript">
18679  */
18680 /**
18681  * @class Roo.tree.TreeEditor
18682  * @extends Roo.Editor
18683  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
18684  * as the editor field.
18685  * @constructor
18686  * @param {Object} config (used to be the tree panel.)
18687  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
18688  * 
18689  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
18690  * @cfg {Roo.form.TextField|Object} field The field configuration
18691  *
18692  * 
18693  */
18694 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
18695     var tree = config;
18696     var field;
18697     if (oldconfig) { // old style..
18698         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
18699     } else {
18700         // new style..
18701         tree = config.tree;
18702         config.field = config.field  || {};
18703         config.field.xtype = 'TextField';
18704         field = Roo.factory(config.field, Roo.form);
18705     }
18706     config = config || {};
18707     
18708     
18709     this.addEvents({
18710         /**
18711          * @event beforenodeedit
18712          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
18713          * false from the handler of this event.
18714          * @param {Editor} this
18715          * @param {Roo.tree.Node} node 
18716          */
18717         "beforenodeedit" : true
18718     });
18719     
18720     //Roo.log(config);
18721     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
18722
18723     this.tree = tree;
18724
18725     tree.on('beforeclick', this.beforeNodeClick, this);
18726     tree.getTreeEl().on('mousedown', this.hide, this);
18727     this.on('complete', this.updateNode, this);
18728     this.on('beforestartedit', this.fitToTree, this);
18729     this.on('startedit', this.bindScroll, this, {delay:10});
18730     this.on('specialkey', this.onSpecialKey, this);
18731 };
18732
18733 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
18734     /**
18735      * @cfg {String} alignment
18736      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
18737      */
18738     alignment: "l-l",
18739     // inherit
18740     autoSize: false,
18741     /**
18742      * @cfg {Boolean} hideEl
18743      * True to hide the bound element while the editor is displayed (defaults to false)
18744      */
18745     hideEl : false,
18746     /**
18747      * @cfg {String} cls
18748      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
18749      */
18750     cls: "x-small-editor x-tree-editor",
18751     /**
18752      * @cfg {Boolean} shim
18753      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
18754      */
18755     shim:false,
18756     // inherit
18757     shadow:"frame",
18758     /**
18759      * @cfg {Number} maxWidth
18760      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
18761      * the containing tree element's size, it will be automatically limited for you to the container width, taking
18762      * scroll and client offsets into account prior to each edit.
18763      */
18764     maxWidth: 250,
18765
18766     editDelay : 350,
18767
18768     // private
18769     fitToTree : function(ed, el){
18770         var td = this.tree.getTreeEl().dom, nd = el.dom;
18771         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
18772             td.scrollLeft = nd.offsetLeft;
18773         }
18774         var w = Math.min(
18775                 this.maxWidth,
18776                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
18777         this.setSize(w, '');
18778         
18779         return this.fireEvent('beforenodeedit', this, this.editNode);
18780         
18781     },
18782
18783     // private
18784     triggerEdit : function(node){
18785         this.completeEdit();
18786         this.editNode = node;
18787         this.startEdit(node.ui.textNode, node.text);
18788     },
18789
18790     // private
18791     bindScroll : function(){
18792         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
18793     },
18794
18795     // private
18796     beforeNodeClick : function(node, e){
18797         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
18798         this.lastClick = new Date();
18799         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
18800             e.stopEvent();
18801             this.triggerEdit(node);
18802             return false;
18803         }
18804         return true;
18805     },
18806
18807     // private
18808     updateNode : function(ed, value){
18809         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
18810         this.editNode.setText(value);
18811     },
18812
18813     // private
18814     onHide : function(){
18815         Roo.tree.TreeEditor.superclass.onHide.call(this);
18816         if(this.editNode){
18817             this.editNode.ui.focus();
18818         }
18819     },
18820
18821     // private
18822     onSpecialKey : function(field, e){
18823         var k = e.getKey();
18824         if(k == e.ESC){
18825             e.stopEvent();
18826             this.cancelEdit();
18827         }else if(k == e.ENTER && !e.hasModifier()){
18828             e.stopEvent();
18829             this.completeEdit();
18830         }
18831     }
18832 });//<Script type="text/javascript">
18833 /*
18834  * Based on:
18835  * Ext JS Library 1.1.1
18836  * Copyright(c) 2006-2007, Ext JS, LLC.
18837  *
18838  * Originally Released Under LGPL - original licence link has changed is not relivant.
18839  *
18840  * Fork - LGPL
18841  * <script type="text/javascript">
18842  */
18843  
18844 /**
18845  * Not documented??? - probably should be...
18846  */
18847
18848 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
18849     //focus: Roo.emptyFn, // prevent odd scrolling behavior
18850     
18851     renderElements : function(n, a, targetNode, bulkRender){
18852         //consel.log("renderElements?");
18853         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18854
18855         var t = n.getOwnerTree();
18856         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
18857         
18858         var cols = t.columns;
18859         var bw = t.borderWidth;
18860         var c = cols[0];
18861         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18862          var cb = typeof a.checked == "boolean";
18863         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18864         var colcls = 'x-t-' + tid + '-c0';
18865         var buf = [
18866             '<li class="x-tree-node">',
18867             
18868                 
18869                 '<div class="x-tree-node-el ', a.cls,'">',
18870                     // extran...
18871                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
18872                 
18873                 
18874                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
18875                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
18876                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
18877                            (a.icon ? ' x-tree-node-inline-icon' : ''),
18878                            (a.iconCls ? ' '+a.iconCls : ''),
18879                            '" unselectable="on" />',
18880                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
18881                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
18882                              
18883                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18884                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
18885                             '<span unselectable="on" qtip="' + tx + '">',
18886                              tx,
18887                              '</span></a>' ,
18888                     '</div>',
18889                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18890                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
18891                  ];
18892         for(var i = 1, len = cols.length; i < len; i++){
18893             c = cols[i];
18894             colcls = 'x-t-' + tid + '-c' +i;
18895             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18896             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
18897                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
18898                       "</div>");
18899          }
18900          
18901          buf.push(
18902             '</a>',
18903             '<div class="x-clear"></div></div>',
18904             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18905             "</li>");
18906         
18907         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18908             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18909                                 n.nextSibling.ui.getEl(), buf.join(""));
18910         }else{
18911             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18912         }
18913         var el = this.wrap.firstChild;
18914         this.elRow = el;
18915         this.elNode = el.firstChild;
18916         this.ranchor = el.childNodes[1];
18917         this.ctNode = this.wrap.childNodes[1];
18918         var cs = el.firstChild.childNodes;
18919         this.indentNode = cs[0];
18920         this.ecNode = cs[1];
18921         this.iconNode = cs[2];
18922         var index = 3;
18923         if(cb){
18924             this.checkbox = cs[3];
18925             index++;
18926         }
18927         this.anchor = cs[index];
18928         
18929         this.textNode = cs[index].firstChild;
18930         
18931         //el.on("click", this.onClick, this);
18932         //el.on("dblclick", this.onDblClick, this);
18933         
18934         
18935        // console.log(this);
18936     },
18937     initEvents : function(){
18938         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
18939         
18940             
18941         var a = this.ranchor;
18942
18943         var el = Roo.get(a);
18944
18945         if(Roo.isOpera){ // opera render bug ignores the CSS
18946             el.setStyle("text-decoration", "none");
18947         }
18948
18949         el.on("click", this.onClick, this);
18950         el.on("dblclick", this.onDblClick, this);
18951         el.on("contextmenu", this.onContextMenu, this);
18952         
18953     },
18954     
18955     /*onSelectedChange : function(state){
18956         if(state){
18957             this.focus();
18958             this.addClass("x-tree-selected");
18959         }else{
18960             //this.blur();
18961             this.removeClass("x-tree-selected");
18962         }
18963     },*/
18964     addClass : function(cls){
18965         if(this.elRow){
18966             Roo.fly(this.elRow).addClass(cls);
18967         }
18968         
18969     },
18970     
18971     
18972     removeClass : function(cls){
18973         if(this.elRow){
18974             Roo.fly(this.elRow).removeClass(cls);
18975         }
18976     }
18977
18978     
18979     
18980 });//<Script type="text/javascript">
18981
18982 /*
18983  * Based on:
18984  * Ext JS Library 1.1.1
18985  * Copyright(c) 2006-2007, Ext JS, LLC.
18986  *
18987  * Originally Released Under LGPL - original licence link has changed is not relivant.
18988  *
18989  * Fork - LGPL
18990  * <script type="text/javascript">
18991  */
18992  
18993
18994 /**
18995  * @class Roo.tree.ColumnTree
18996  * @extends Roo.data.TreePanel
18997  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
18998  * @cfg {int} borderWidth  compined right/left border allowance
18999  * @constructor
19000  * @param {String/HTMLElement/Element} el The container element
19001  * @param {Object} config
19002  */
19003 Roo.tree.ColumnTree =  function(el, config)
19004 {
19005    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19006    this.addEvents({
19007         /**
19008         * @event resize
19009         * Fire this event on a container when it resizes
19010         * @param {int} w Width
19011         * @param {int} h Height
19012         */
19013        "resize" : true
19014     });
19015     this.on('resize', this.onResize, this);
19016 };
19017
19018 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19019     //lines:false,
19020     
19021     
19022     borderWidth: Roo.isBorderBox ? 0 : 2, 
19023     headEls : false,
19024     
19025     render : function(){
19026         // add the header.....
19027        
19028         Roo.tree.ColumnTree.superclass.render.apply(this);
19029         
19030         this.el.addClass('x-column-tree');
19031         
19032         this.headers = this.el.createChild(
19033             {cls:'x-tree-headers'},this.innerCt.dom);
19034    
19035         var cols = this.columns, c;
19036         var totalWidth = 0;
19037         this.headEls = [];
19038         var  len = cols.length;
19039         for(var i = 0; i < len; i++){
19040              c = cols[i];
19041              totalWidth += c.width;
19042             this.headEls.push(this.headers.createChild({
19043                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19044                  cn: {
19045                      cls:'x-tree-hd-text',
19046                      html: c.header
19047                  },
19048                  style:'width:'+(c.width-this.borderWidth)+'px;'
19049              }));
19050         }
19051         this.headers.createChild({cls:'x-clear'});
19052         // prevent floats from wrapping when clipped
19053         this.headers.setWidth(totalWidth);
19054         //this.innerCt.setWidth(totalWidth);
19055         this.innerCt.setStyle({ overflow: 'auto' });
19056         this.onResize(this.width, this.height);
19057              
19058         
19059     },
19060     onResize : function(w,h)
19061     {
19062         this.height = h;
19063         this.width = w;
19064         // resize cols..
19065         this.innerCt.setWidth(this.width);
19066         this.innerCt.setHeight(this.height-20);
19067         
19068         // headers...
19069         var cols = this.columns, c;
19070         var totalWidth = 0;
19071         var expEl = false;
19072         var len = cols.length;
19073         for(var i = 0; i < len; i++){
19074             c = cols[i];
19075             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19076                 // it's the expander..
19077                 expEl  = this.headEls[i];
19078                 continue;
19079             }
19080             totalWidth += c.width;
19081             
19082         }
19083         if (expEl) {
19084             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19085         }
19086         this.headers.setWidth(w-20);
19087
19088         
19089         
19090         
19091     }
19092 });
19093 /*
19094  * Based on:
19095  * Ext JS Library 1.1.1
19096  * Copyright(c) 2006-2007, Ext JS, LLC.
19097  *
19098  * Originally Released Under LGPL - original licence link has changed is not relivant.
19099  *
19100  * Fork - LGPL
19101  * <script type="text/javascript">
19102  */
19103  
19104 /**
19105  * @class Roo.menu.Menu
19106  * @extends Roo.util.Observable
19107  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19108  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19109  * @constructor
19110  * Creates a new Menu
19111  * @param {Object} config Configuration options
19112  */
19113 Roo.menu.Menu = function(config){
19114     Roo.apply(this, config);
19115     this.id = this.id || Roo.id();
19116     this.addEvents({
19117         /**
19118          * @event beforeshow
19119          * Fires before this menu is displayed
19120          * @param {Roo.menu.Menu} this
19121          */
19122         beforeshow : true,
19123         /**
19124          * @event beforehide
19125          * Fires before this menu is hidden
19126          * @param {Roo.menu.Menu} this
19127          */
19128         beforehide : true,
19129         /**
19130          * @event show
19131          * Fires after this menu is displayed
19132          * @param {Roo.menu.Menu} this
19133          */
19134         show : true,
19135         /**
19136          * @event hide
19137          * Fires after this menu is hidden
19138          * @param {Roo.menu.Menu} this
19139          */
19140         hide : true,
19141         /**
19142          * @event click
19143          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19144          * @param {Roo.menu.Menu} this
19145          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19146          * @param {Roo.EventObject} e
19147          */
19148         click : true,
19149         /**
19150          * @event mouseover
19151          * Fires when the mouse is hovering over this menu
19152          * @param {Roo.menu.Menu} this
19153          * @param {Roo.EventObject} e
19154          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19155          */
19156         mouseover : true,
19157         /**
19158          * @event mouseout
19159          * Fires when the mouse exits this menu
19160          * @param {Roo.menu.Menu} this
19161          * @param {Roo.EventObject} e
19162          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19163          */
19164         mouseout : true,
19165         /**
19166          * @event itemclick
19167          * Fires when a menu item contained in this menu is clicked
19168          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19169          * @param {Roo.EventObject} e
19170          */
19171         itemclick: true
19172     });
19173     if (this.registerMenu) {
19174         Roo.menu.MenuMgr.register(this);
19175     }
19176     
19177     var mis = this.items;
19178     this.items = new Roo.util.MixedCollection();
19179     if(mis){
19180         this.add.apply(this, mis);
19181     }
19182 };
19183
19184 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19185     /**
19186      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19187      */
19188     minWidth : 120,
19189     /**
19190      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19191      * for bottom-right shadow (defaults to "sides")
19192      */
19193     shadow : "sides",
19194     /**
19195      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19196      * this menu (defaults to "tl-tr?")
19197      */
19198     subMenuAlign : "tl-tr?",
19199     /**
19200      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19201      * relative to its element of origin (defaults to "tl-bl?")
19202      */
19203     defaultAlign : "tl-bl?",
19204     /**
19205      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19206      */
19207     allowOtherMenus : false,
19208     /**
19209      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19210      */
19211     registerMenu : true,
19212
19213     hidden:true,
19214
19215     // private
19216     render : function(){
19217         if(this.el){
19218             return;
19219         }
19220         var el = this.el = new Roo.Layer({
19221             cls: "x-menu",
19222             shadow:this.shadow,
19223             constrain: false,
19224             parentEl: this.parentEl || document.body,
19225             zindex:15000
19226         });
19227
19228         this.keyNav = new Roo.menu.MenuNav(this);
19229
19230         if(this.plain){
19231             el.addClass("x-menu-plain");
19232         }
19233         if(this.cls){
19234             el.addClass(this.cls);
19235         }
19236         // generic focus element
19237         this.focusEl = el.createChild({
19238             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19239         });
19240         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19241         //disabling touch- as it's causing issues ..
19242         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
19243         ul.on('click'   , this.onClick, this);
19244         
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                 btn('strikethrough')
27182             );
27183         };
27184         if(!this.disable.fontSize){
27185             tb.add(
27186                 '-',
27187                 
27188                 
27189                 btn('increasefontsize', false, editorcore.adjustFont),
27190                 btn('decreasefontsize', false, editorcore.adjustFont)
27191             );
27192         };
27193         
27194         
27195         if(!this.disable.colors){
27196             tb.add(
27197                 '-', {
27198                     id:editorcore.frameId +'-forecolor',
27199                     cls:'x-btn-icon x-edit-forecolor',
27200                     clickEvent:'mousedown',
27201                     tooltip: this.buttonTips['forecolor'] || undefined,
27202                     tabIndex:-1,
27203                     menu : new Roo.menu.ColorMenu({
27204                         allowReselect: true,
27205                         focus: Roo.emptyFn,
27206                         value:'000000',
27207                         plain:true,
27208                         selectHandler: function(cp, color){
27209                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
27210                             editor.deferFocus();
27211                         },
27212                         scope: editorcore,
27213                         clickEvent:'mousedown'
27214                     })
27215                 }, {
27216                     id:editorcore.frameId +'backcolor',
27217                     cls:'x-btn-icon x-edit-backcolor',
27218                     clickEvent:'mousedown',
27219                     tooltip: this.buttonTips['backcolor'] || undefined,
27220                     tabIndex:-1,
27221                     menu : new Roo.menu.ColorMenu({
27222                         focus: Roo.emptyFn,
27223                         value:'FFFFFF',
27224                         plain:true,
27225                         allowReselect: true,
27226                         selectHandler: function(cp, color){
27227                             if(Roo.isGecko){
27228                                 editorcore.execCmd('useCSS', false);
27229                                 editorcore.execCmd('hilitecolor', color);
27230                                 editorcore.execCmd('useCSS', true);
27231                                 editor.deferFocus();
27232                             }else{
27233                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
27234                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
27235                                 editor.deferFocus();
27236                             }
27237                         },
27238                         scope:editorcore,
27239                         clickEvent:'mousedown'
27240                     })
27241                 }
27242             );
27243         };
27244         // now add all the items...
27245         
27246
27247         if(!this.disable.alignments){
27248             tb.add(
27249                 '-',
27250                 btn('justifyleft'),
27251                 btn('justifycenter'),
27252                 btn('justifyright')
27253             );
27254         };
27255
27256         //if(!Roo.isSafari){
27257             if(!this.disable.links){
27258                 tb.add(
27259                     '-',
27260                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
27261                 );
27262             };
27263
27264             if(!this.disable.lists){
27265                 tb.add(
27266                     '-',
27267                     btn('insertorderedlist'),
27268                     btn('insertunorderedlist')
27269                 );
27270             }
27271             if(!this.disable.sourceEdit){
27272                 tb.add(
27273                     '-',
27274                     btn('sourceedit', true, function(btn){
27275                         this.toggleSourceEdit(btn.pressed);
27276                     })
27277                 );
27278             }
27279         //}
27280         
27281         var smenu = { };
27282         // special menu.. - needs to be tidied up..
27283         if (!this.disable.special) {
27284             smenu = {
27285                 text: "&#169;",
27286                 cls: 'x-edit-none',
27287                 
27288                 menu : {
27289                     items : []
27290                 }
27291             };
27292             for (var i =0; i < this.specialChars.length; i++) {
27293                 smenu.menu.items.push({
27294                     
27295                     html: this.specialChars[i],
27296                     handler: function(a,b) {
27297                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
27298                         //editor.insertAtCursor(a.html);
27299                         
27300                     },
27301                     tabIndex:-1
27302                 });
27303             }
27304             
27305             
27306             tb.add(smenu);
27307             
27308             
27309         }
27310         
27311         var cmenu = { };
27312         if (!this.disable.cleanStyles) {
27313             cmenu = {
27314                 cls: 'x-btn-icon x-btn-clear',
27315                 
27316                 menu : {
27317                     items : []
27318                 }
27319             };
27320             for (var i =0; i < this.cleanStyles.length; i++) {
27321                 cmenu.menu.items.push({
27322                     actiontype : this.cleanStyles[i],
27323                     html: 'Remove ' + this.cleanStyles[i],
27324                     handler: function(a,b) {
27325 //                        Roo.log(a);
27326 //                        Roo.log(b);
27327                         var c = Roo.get(editorcore.doc.body);
27328                         c.select('[style]').each(function(s) {
27329                             s.dom.style.removeProperty(a.actiontype);
27330                         });
27331                         editorcore.syncValue();
27332                     },
27333                     tabIndex:-1
27334                 });
27335             }
27336              cmenu.menu.items.push({
27337                 actiontype : 'tablewidths',
27338                 html: 'Remove Table Widths',
27339                 handler: function(a,b) {
27340                     editorcore.cleanTableWidths();
27341                     editorcore.syncValue();
27342                 },
27343                 tabIndex:-1
27344             });
27345             cmenu.menu.items.push({
27346                 actiontype : 'word',
27347                 html: 'Remove MS Word Formating',
27348                 handler: function(a,b) {
27349                     editorcore.cleanWord();
27350                     editorcore.syncValue();
27351                 },
27352                 tabIndex:-1
27353             });
27354             
27355             cmenu.menu.items.push({
27356                 actiontype : 'all',
27357                 html: 'Remove All Styles',
27358                 handler: function(a,b) {
27359                     
27360                     var c = Roo.get(editorcore.doc.body);
27361                     c.select('[style]').each(function(s) {
27362                         s.dom.removeAttribute('style');
27363                     });
27364                     editorcore.syncValue();
27365                 },
27366                 tabIndex:-1
27367             });
27368             
27369             cmenu.menu.items.push({
27370                 actiontype : 'all',
27371                 html: 'Remove All CSS Classes',
27372                 handler: function(a,b) {
27373                     
27374                     var c = Roo.get(editorcore.doc.body);
27375                     c.select('[class]').each(function(s) {
27376                         s.dom.className = '';
27377                     });
27378                     editorcore.syncValue();
27379                 },
27380                 tabIndex:-1
27381             });
27382             
27383              cmenu.menu.items.push({
27384                 actiontype : 'tidy',
27385                 html: 'Tidy HTML Source',
27386                 handler: function(a,b) {
27387                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
27388                     editorcore.syncValue();
27389                 },
27390                 tabIndex:-1
27391             });
27392             
27393             
27394             tb.add(cmenu);
27395         }
27396          
27397         if (!this.disable.specialElements) {
27398             var semenu = {
27399                 text: "Other;",
27400                 cls: 'x-edit-none',
27401                 menu : {
27402                     items : []
27403                 }
27404             };
27405             for (var i =0; i < this.specialElements.length; i++) {
27406                 semenu.menu.items.push(
27407                     Roo.apply({ 
27408                         handler: function(a,b) {
27409                             editor.insertAtCursor(this.ihtml);
27410                         }
27411                     }, this.specialElements[i])
27412                 );
27413                     
27414             }
27415             
27416             tb.add(semenu);
27417             
27418             
27419         }
27420          
27421         
27422         if (this.btns) {
27423             for(var i =0; i< this.btns.length;i++) {
27424                 var b = Roo.factory(this.btns[i],Roo.form);
27425                 b.cls =  'x-edit-none';
27426                 
27427                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
27428                     b.cls += ' x-init-enable';
27429                 }
27430                 
27431                 b.scope = editorcore;
27432                 tb.add(b);
27433             }
27434         
27435         }
27436         
27437         
27438         
27439         // disable everything...
27440         
27441         this.tb.items.each(function(item){
27442             
27443            if(
27444                 item.id != editorcore.frameId+ '-sourceedit' && 
27445                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
27446             ){
27447                 
27448                 item.disable();
27449             }
27450         });
27451         this.rendered = true;
27452         
27453         // the all the btns;
27454         editor.on('editorevent', this.updateToolbar, this);
27455         // other toolbars need to implement this..
27456         //editor.on('editmodechange', this.updateToolbar, this);
27457     },
27458     
27459     
27460     relayBtnCmd : function(btn) {
27461         this.editorcore.relayCmd(btn.cmd);
27462     },
27463     // private used internally
27464     createLink : function(){
27465         Roo.log("create link?");
27466         var url = prompt(this.createLinkText, this.defaultLinkValue);
27467         if(url && url != 'http:/'+'/'){
27468             this.editorcore.relayCmd('createlink', url);
27469         }
27470     },
27471
27472     
27473     /**
27474      * Protected method that will not generally be called directly. It triggers
27475      * a toolbar update by reading the markup state of the current selection in the editor.
27476      */
27477     updateToolbar: function(){
27478
27479         if(!this.editorcore.activated){
27480             this.editor.onFirstFocus();
27481             return;
27482         }
27483
27484         var btns = this.tb.items.map, 
27485             doc = this.editorcore.doc,
27486             frameId = this.editorcore.frameId;
27487
27488         if(!this.disable.font && !Roo.isSafari){
27489             /*
27490             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
27491             if(name != this.fontSelect.dom.value){
27492                 this.fontSelect.dom.value = name;
27493             }
27494             */
27495         }
27496         if(!this.disable.format){
27497             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
27498             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
27499             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
27500             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
27501         }
27502         if(!this.disable.alignments){
27503             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
27504             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
27505             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
27506         }
27507         if(!Roo.isSafari && !this.disable.lists){
27508             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
27509             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
27510         }
27511         
27512         var ans = this.editorcore.getAllAncestors();
27513         if (this.formatCombo) {
27514             
27515             
27516             var store = this.formatCombo.store;
27517             this.formatCombo.setValue("");
27518             for (var i =0; i < ans.length;i++) {
27519                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27520                     // select it..
27521                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27522                     break;
27523                 }
27524             }
27525         }
27526         
27527         
27528         
27529         // hides menus... - so this cant be on a menu...
27530         Roo.menu.MenuMgr.hideAll();
27531
27532         //this.editorsyncValue();
27533     },
27534    
27535     
27536     createFontOptions : function(){
27537         var buf = [], fs = this.fontFamilies, ff, lc;
27538         
27539         
27540         
27541         for(var i = 0, len = fs.length; i< len; i++){
27542             ff = fs[i];
27543             lc = ff.toLowerCase();
27544             buf.push(
27545                 '<option value="',lc,'" style="font-family:',ff,';"',
27546                     (this.defaultFont == lc ? ' selected="true">' : '>'),
27547                     ff,
27548                 '</option>'
27549             );
27550         }
27551         return buf.join('');
27552     },
27553     
27554     toggleSourceEdit : function(sourceEditMode){
27555         
27556         Roo.log("toolbar toogle");
27557         if(sourceEditMode === undefined){
27558             sourceEditMode = !this.sourceEditMode;
27559         }
27560         this.sourceEditMode = sourceEditMode === true;
27561         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
27562         // just toggle the button?
27563         if(btn.pressed !== this.sourceEditMode){
27564             btn.toggle(this.sourceEditMode);
27565             return;
27566         }
27567         
27568         if(sourceEditMode){
27569             Roo.log("disabling buttons");
27570             this.tb.items.each(function(item){
27571                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
27572                     item.disable();
27573                 }
27574             });
27575           
27576         }else{
27577             Roo.log("enabling buttons");
27578             if(this.editorcore.initialized){
27579                 this.tb.items.each(function(item){
27580                     item.enable();
27581                 });
27582             }
27583             
27584         }
27585         Roo.log("calling toggole on editor");
27586         // tell the editor that it's been pressed..
27587         this.editor.toggleSourceEdit(sourceEditMode);
27588        
27589     },
27590      /**
27591      * Object collection of toolbar tooltips for the buttons in the editor. The key
27592      * is the command id associated with that button and the value is a valid QuickTips object.
27593      * For example:
27594 <pre><code>
27595 {
27596     bold : {
27597         title: 'Bold (Ctrl+B)',
27598         text: 'Make the selected text bold.',
27599         cls: 'x-html-editor-tip'
27600     },
27601     italic : {
27602         title: 'Italic (Ctrl+I)',
27603         text: 'Make the selected text italic.',
27604         cls: 'x-html-editor-tip'
27605     },
27606     ...
27607 </code></pre>
27608     * @type Object
27609      */
27610     buttonTips : {
27611         bold : {
27612             title: 'Bold (Ctrl+B)',
27613             text: 'Make the selected text bold.',
27614             cls: 'x-html-editor-tip'
27615         },
27616         italic : {
27617             title: 'Italic (Ctrl+I)',
27618             text: 'Make the selected text italic.',
27619             cls: 'x-html-editor-tip'
27620         },
27621         underline : {
27622             title: 'Underline (Ctrl+U)',
27623             text: 'Underline the selected text.',
27624             cls: 'x-html-editor-tip'
27625         },
27626         strikethrough : {
27627             title: 'Strikethrough',
27628             text: 'Strikethrough the selected text.',
27629             cls: 'x-html-editor-tip'
27630         },
27631         increasefontsize : {
27632             title: 'Grow Text',
27633             text: 'Increase the font size.',
27634             cls: 'x-html-editor-tip'
27635         },
27636         decreasefontsize : {
27637             title: 'Shrink Text',
27638             text: 'Decrease the font size.',
27639             cls: 'x-html-editor-tip'
27640         },
27641         backcolor : {
27642             title: 'Text Highlight Color',
27643             text: 'Change the background color of the selected text.',
27644             cls: 'x-html-editor-tip'
27645         },
27646         forecolor : {
27647             title: 'Font Color',
27648             text: 'Change the color of the selected text.',
27649             cls: 'x-html-editor-tip'
27650         },
27651         justifyleft : {
27652             title: 'Align Text Left',
27653             text: 'Align text to the left.',
27654             cls: 'x-html-editor-tip'
27655         },
27656         justifycenter : {
27657             title: 'Center Text',
27658             text: 'Center text in the editor.',
27659             cls: 'x-html-editor-tip'
27660         },
27661         justifyright : {
27662             title: 'Align Text Right',
27663             text: 'Align text to the right.',
27664             cls: 'x-html-editor-tip'
27665         },
27666         insertunorderedlist : {
27667             title: 'Bullet List',
27668             text: 'Start a bulleted list.',
27669             cls: 'x-html-editor-tip'
27670         },
27671         insertorderedlist : {
27672             title: 'Numbered List',
27673             text: 'Start a numbered list.',
27674             cls: 'x-html-editor-tip'
27675         },
27676         createlink : {
27677             title: 'Hyperlink',
27678             text: 'Make the selected text a hyperlink.',
27679             cls: 'x-html-editor-tip'
27680         },
27681         sourceedit : {
27682             title: 'Source Edit',
27683             text: 'Switch to source editing mode.',
27684             cls: 'x-html-editor-tip'
27685         }
27686     },
27687     // private
27688     onDestroy : function(){
27689         if(this.rendered){
27690             
27691             this.tb.items.each(function(item){
27692                 if(item.menu){
27693                     item.menu.removeAll();
27694                     if(item.menu.el){
27695                         item.menu.el.destroy();
27696                     }
27697                 }
27698                 item.destroy();
27699             });
27700              
27701         }
27702     },
27703     onFirstFocus: function() {
27704         this.tb.items.each(function(item){
27705            item.enable();
27706         });
27707     }
27708 });
27709
27710
27711
27712
27713 // <script type="text/javascript">
27714 /*
27715  * Based on
27716  * Ext JS Library 1.1.1
27717  * Copyright(c) 2006-2007, Ext JS, LLC.
27718  *  
27719  
27720  */
27721
27722  
27723 /**
27724  * @class Roo.form.HtmlEditor.ToolbarContext
27725  * Context Toolbar
27726  * 
27727  * Usage:
27728  *
27729  new Roo.form.HtmlEditor({
27730     ....
27731     toolbars : [
27732         { xtype: 'ToolbarStandard', styles : {} }
27733         { xtype: 'ToolbarContext', disable : {} }
27734     ]
27735 })
27736
27737      
27738  * 
27739  * @config : {Object} disable List of elements to disable.. (not done yet.)
27740  * @config : {Object} styles  Map of styles available.
27741  * 
27742  */
27743
27744 Roo.form.HtmlEditor.ToolbarContext = function(config)
27745 {
27746     
27747     Roo.apply(this, config);
27748     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27749     // dont call parent... till later.
27750     this.styles = this.styles || {};
27751 }
27752
27753  
27754
27755 Roo.form.HtmlEditor.ToolbarContext.types = {
27756     'IMG' : {
27757         width : {
27758             title: "Width",
27759             width: 40
27760         },
27761         height:  {
27762             title: "Height",
27763             width: 40
27764         },
27765         align: {
27766             title: "Align",
27767             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
27768             width : 80
27769             
27770         },
27771         border: {
27772             title: "Border",
27773             width: 40
27774         },
27775         alt: {
27776             title: "Alt",
27777             width: 120
27778         },
27779         src : {
27780             title: "Src",
27781             width: 220
27782         }
27783         
27784     },
27785     'A' : {
27786         name : {
27787             title: "Name",
27788             width: 50
27789         },
27790         target:  {
27791             title: "Target",
27792             width: 120
27793         },
27794         href:  {
27795             title: "Href",
27796             width: 220
27797         } // border?
27798         
27799     },
27800     'TABLE' : {
27801         rows : {
27802             title: "Rows",
27803             width: 20
27804         },
27805         cols : {
27806             title: "Cols",
27807             width: 20
27808         },
27809         width : {
27810             title: "Width",
27811             width: 40
27812         },
27813         height : {
27814             title: "Height",
27815             width: 40
27816         },
27817         border : {
27818             title: "Border",
27819             width: 20
27820         }
27821     },
27822     'TD' : {
27823         width : {
27824             title: "Width",
27825             width: 40
27826         },
27827         height : {
27828             title: "Height",
27829             width: 40
27830         },   
27831         align: {
27832             title: "Align",
27833             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27834             width: 80
27835         },
27836         valign: {
27837             title: "Valign",
27838             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27839             width: 80
27840         },
27841         colspan: {
27842             title: "Colspan",
27843             width: 20
27844             
27845         },
27846          'font-family'  : {
27847             title : "Font",
27848             style : 'fontFamily',
27849             displayField: 'display',
27850             optname : 'font-family',
27851             width: 140
27852         }
27853     },
27854     'INPUT' : {
27855         name : {
27856             title: "name",
27857             width: 120
27858         },
27859         value : {
27860             title: "Value",
27861             width: 120
27862         },
27863         width : {
27864             title: "Width",
27865             width: 40
27866         }
27867     },
27868     'LABEL' : {
27869         'for' : {
27870             title: "For",
27871             width: 120
27872         }
27873     },
27874     'TEXTAREA' : {
27875           name : {
27876             title: "name",
27877             width: 120
27878         },
27879         rows : {
27880             title: "Rows",
27881             width: 20
27882         },
27883         cols : {
27884             title: "Cols",
27885             width: 20
27886         }
27887     },
27888     'SELECT' : {
27889         name : {
27890             title: "name",
27891             width: 120
27892         },
27893         selectoptions : {
27894             title: "Options",
27895             width: 200
27896         }
27897     },
27898     
27899     // should we really allow this??
27900     // should this just be 
27901     'BODY' : {
27902         title : {
27903             title: "Title",
27904             width: 200,
27905             disabled : true
27906         }
27907     },
27908     'SPAN' : {
27909         'font-family'  : {
27910             title : "Font",
27911             style : 'fontFamily',
27912             displayField: 'display',
27913             optname : 'font-family',
27914             width: 140
27915         }
27916     },
27917     'DIV' : {
27918         'font-family'  : {
27919             title : "Font",
27920             style : 'fontFamily',
27921             displayField: 'display',
27922             optname : 'font-family',
27923             width: 140
27924         }
27925     },
27926      'P' : {
27927         'font-family'  : {
27928             title : "Font",
27929             style : 'fontFamily',
27930             displayField: 'display',
27931             optname : 'font-family',
27932             width: 140
27933         }
27934     },
27935     
27936     '*' : {
27937         // empty..
27938     }
27939
27940 };
27941
27942 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
27943 Roo.form.HtmlEditor.ToolbarContext.stores = false;
27944
27945 Roo.form.HtmlEditor.ToolbarContext.options = {
27946         'font-family'  : [ 
27947                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
27948                 [ 'Courier New', 'Courier New'],
27949                 [ 'Tahoma', 'Tahoma'],
27950                 [ 'Times New Roman,serif', 'Times'],
27951                 [ 'Verdana','Verdana' ]
27952         ]
27953 };
27954
27955 // fixme - these need to be configurable..
27956  
27957
27958 //Roo.form.HtmlEditor.ToolbarContext.types
27959
27960
27961 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
27962     
27963     tb: false,
27964     
27965     rendered: false,
27966     
27967     editor : false,
27968     editorcore : false,
27969     /**
27970      * @cfg {Object} disable  List of toolbar elements to disable
27971          
27972      */
27973     disable : false,
27974     /**
27975      * @cfg {Object} styles List of styles 
27976      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
27977      *
27978      * These must be defined in the page, so they get rendered correctly..
27979      * .headline { }
27980      * TD.underline { }
27981      * 
27982      */
27983     styles : false,
27984     
27985     options: false,
27986     
27987     toolbars : false,
27988     
27989     init : function(editor)
27990     {
27991         this.editor = editor;
27992         this.editorcore = editor.editorcore ? editor.editorcore : editor;
27993         var editorcore = this.editorcore;
27994         
27995         var fid = editorcore.frameId;
27996         var etb = this;
27997         function btn(id, toggle, handler){
27998             var xid = fid + '-'+ id ;
27999             return {
28000                 id : xid,
28001                 cmd : id,
28002                 cls : 'x-btn-icon x-edit-'+id,
28003                 enableToggle:toggle !== false,
28004                 scope: editorcore, // was editor...
28005                 handler:handler||editorcore.relayBtnCmd,
28006                 clickEvent:'mousedown',
28007                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
28008                 tabIndex:-1
28009             };
28010         }
28011         // create a new element.
28012         var wdiv = editor.wrap.createChild({
28013                 tag: 'div'
28014             }, editor.wrap.dom.firstChild.nextSibling, true);
28015         
28016         // can we do this more than once??
28017         
28018          // stop form submits
28019       
28020  
28021         // disable everything...
28022         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
28023         this.toolbars = {};
28024            
28025         for (var i in  ty) {
28026           
28027             this.toolbars[i] = this.buildToolbar(ty[i],i);
28028         }
28029         this.tb = this.toolbars.BODY;
28030         this.tb.el.show();
28031         this.buildFooter();
28032         this.footer.show();
28033         editor.on('hide', function( ) { this.footer.hide() }, this);
28034         editor.on('show', function( ) { this.footer.show() }, this);
28035         
28036          
28037         this.rendered = true;
28038         
28039         // the all the btns;
28040         editor.on('editorevent', this.updateToolbar, this);
28041         // other toolbars need to implement this..
28042         //editor.on('editmodechange', this.updateToolbar, this);
28043     },
28044     
28045     
28046     
28047     /**
28048      * Protected method that will not generally be called directly. It triggers
28049      * a toolbar update by reading the markup state of the current selection in the editor.
28050      *
28051      * Note you can force an update by calling on('editorevent', scope, false)
28052      */
28053     updateToolbar: function(editor,ev,sel){
28054
28055         //Roo.log(ev);
28056         // capture mouse up - this is handy for selecting images..
28057         // perhaps should go somewhere else...
28058         if(!this.editorcore.activated){
28059              this.editor.onFirstFocus();
28060             return;
28061         }
28062         
28063         
28064         
28065         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
28066         // selectNode - might want to handle IE?
28067         if (ev &&
28068             (ev.type == 'mouseup' || ev.type == 'click' ) &&
28069             ev.target && ev.target.tagName == 'IMG') {
28070             // they have click on an image...
28071             // let's see if we can change the selection...
28072             sel = ev.target;
28073          
28074               var nodeRange = sel.ownerDocument.createRange();
28075             try {
28076                 nodeRange.selectNode(sel);
28077             } catch (e) {
28078                 nodeRange.selectNodeContents(sel);
28079             }
28080             //nodeRange.collapse(true);
28081             var s = this.editorcore.win.getSelection();
28082             s.removeAllRanges();
28083             s.addRange(nodeRange);
28084         }  
28085         
28086       
28087         var updateFooter = sel ? false : true;
28088         
28089         
28090         var ans = this.editorcore.getAllAncestors();
28091         
28092         // pick
28093         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
28094         
28095         if (!sel) { 
28096             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
28097             sel = sel ? sel : this.editorcore.doc.body;
28098             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
28099             
28100         }
28101         // pick a menu that exists..
28102         var tn = sel.tagName.toUpperCase();
28103         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
28104         
28105         tn = sel.tagName.toUpperCase();
28106         
28107         var lastSel = this.tb.selectedNode;
28108         
28109         this.tb.selectedNode = sel;
28110         
28111         // if current menu does not match..
28112         
28113         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
28114                 
28115             this.tb.el.hide();
28116             ///console.log("show: " + tn);
28117             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
28118             this.tb.el.show();
28119             // update name
28120             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
28121             
28122             
28123             // update attributes
28124             if (this.tb.fields) {
28125                 this.tb.fields.each(function(e) {
28126                     if (e.stylename) {
28127                         e.setValue(sel.style[e.stylename]);
28128                         return;
28129                     } 
28130                    e.setValue(sel.getAttribute(e.attrname));
28131                 });
28132             }
28133             
28134             var hasStyles = false;
28135             for(var i in this.styles) {
28136                 hasStyles = true;
28137                 break;
28138             }
28139             
28140             // update styles
28141             if (hasStyles) { 
28142                 var st = this.tb.fields.item(0);
28143                 
28144                 st.store.removeAll();
28145                
28146                 
28147                 var cn = sel.className.split(/\s+/);
28148                 
28149                 var avs = [];
28150                 if (this.styles['*']) {
28151                     
28152                     Roo.each(this.styles['*'], function(v) {
28153                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
28154                     });
28155                 }
28156                 if (this.styles[tn]) { 
28157                     Roo.each(this.styles[tn], function(v) {
28158                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
28159                     });
28160                 }
28161                 
28162                 st.store.loadData(avs);
28163                 st.collapse();
28164                 st.setValue(cn);
28165             }
28166             // flag our selected Node.
28167             this.tb.selectedNode = sel;
28168            
28169            
28170             Roo.menu.MenuMgr.hideAll();
28171
28172         }
28173         
28174         if (!updateFooter) {
28175             //this.footDisp.dom.innerHTML = ''; 
28176             return;
28177         }
28178         // update the footer
28179         //
28180         var html = '';
28181         
28182         this.footerEls = ans.reverse();
28183         Roo.each(this.footerEls, function(a,i) {
28184             if (!a) { return; }
28185             html += html.length ? ' &gt; '  :  '';
28186             
28187             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
28188             
28189         });
28190        
28191         // 
28192         var sz = this.footDisp.up('td').getSize();
28193         this.footDisp.dom.style.width = (sz.width -10) + 'px';
28194         this.footDisp.dom.style.marginLeft = '5px';
28195         
28196         this.footDisp.dom.style.overflow = 'hidden';
28197         
28198         this.footDisp.dom.innerHTML = html;
28199             
28200         //this.editorsyncValue();
28201     },
28202      
28203     
28204    
28205        
28206     // private
28207     onDestroy : function(){
28208         if(this.rendered){
28209             
28210             this.tb.items.each(function(item){
28211                 if(item.menu){
28212                     item.menu.removeAll();
28213                     if(item.menu.el){
28214                         item.menu.el.destroy();
28215                     }
28216                 }
28217                 item.destroy();
28218             });
28219              
28220         }
28221     },
28222     onFirstFocus: function() {
28223         // need to do this for all the toolbars..
28224         this.tb.items.each(function(item){
28225            item.enable();
28226         });
28227     },
28228     buildToolbar: function(tlist, nm)
28229     {
28230         var editor = this.editor;
28231         var editorcore = this.editorcore;
28232          // create a new element.
28233         var wdiv = editor.wrap.createChild({
28234                 tag: 'div'
28235             }, editor.wrap.dom.firstChild.nextSibling, true);
28236         
28237        
28238         var tb = new Roo.Toolbar(wdiv);
28239         // add the name..
28240         
28241         tb.add(nm+ ":&nbsp;");
28242         
28243         var styles = [];
28244         for(var i in this.styles) {
28245             styles.push(i);
28246         }
28247         
28248         // styles...
28249         if (styles && styles.length) {
28250             
28251             // this needs a multi-select checkbox...
28252             tb.addField( new Roo.form.ComboBox({
28253                 store: new Roo.data.SimpleStore({
28254                     id : 'val',
28255                     fields: ['val', 'selected'],
28256                     data : [] 
28257                 }),
28258                 name : '-roo-edit-className',
28259                 attrname : 'className',
28260                 displayField: 'val',
28261                 typeAhead: false,
28262                 mode: 'local',
28263                 editable : false,
28264                 triggerAction: 'all',
28265                 emptyText:'Select Style',
28266                 selectOnFocus:true,
28267                 width: 130,
28268                 listeners : {
28269                     'select': function(c, r, i) {
28270                         // initial support only for on class per el..
28271                         tb.selectedNode.className =  r ? r.get('val') : '';
28272                         editorcore.syncValue();
28273                     }
28274                 }
28275     
28276             }));
28277         }
28278         
28279         var tbc = Roo.form.HtmlEditor.ToolbarContext;
28280         var tbops = tbc.options;
28281         
28282         for (var i in tlist) {
28283             
28284             var item = tlist[i];
28285             tb.add(item.title + ":&nbsp;");
28286             
28287             
28288             //optname == used so you can configure the options available..
28289             var opts = item.opts ? item.opts : false;
28290             if (item.optname) {
28291                 opts = tbops[item.optname];
28292            
28293             }
28294             
28295             if (opts) {
28296                 // opts == pulldown..
28297                 tb.addField( new Roo.form.ComboBox({
28298                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
28299                         id : 'val',
28300                         fields: ['val', 'display'],
28301                         data : opts  
28302                     }),
28303                     name : '-roo-edit-' + i,
28304                     attrname : i,
28305                     stylename : item.style ? item.style : false,
28306                     displayField: item.displayField ? item.displayField : 'val',
28307                     valueField :  'val',
28308                     typeAhead: false,
28309                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
28310                     editable : false,
28311                     triggerAction: 'all',
28312                     emptyText:'Select',
28313                     selectOnFocus:true,
28314                     width: item.width ? item.width  : 130,
28315                     listeners : {
28316                         'select': function(c, r, i) {
28317                             if (c.stylename) {
28318                                 tb.selectedNode.style[c.stylename] =  r.get('val');
28319                                 return;
28320                             }
28321                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
28322                         }
28323                     }
28324
28325                 }));
28326                 continue;
28327                     
28328                  
28329                 
28330                 tb.addField( new Roo.form.TextField({
28331                     name: i,
28332                     width: 100,
28333                     //allowBlank:false,
28334                     value: ''
28335                 }));
28336                 continue;
28337             }
28338             tb.addField( new Roo.form.TextField({
28339                 name: '-roo-edit-' + i,
28340                 attrname : i,
28341                 
28342                 width: item.width,
28343                 //allowBlank:true,
28344                 value: '',
28345                 listeners: {
28346                     'change' : function(f, nv, ov) {
28347                         tb.selectedNode.setAttribute(f.attrname, nv);
28348                     }
28349                 }
28350             }));
28351              
28352         }
28353         
28354         var _this = this;
28355         
28356         if(nm == 'BODY'){
28357             tb.addSeparator();
28358         
28359             tb.addButton( {
28360                 text: 'Stylesheets',
28361
28362                 listeners : {
28363                     click : function ()
28364                     {
28365                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
28366                     }
28367                 }
28368             });
28369         }
28370         
28371         tb.addFill();
28372         tb.addButton( {
28373             text: 'Remove Tag',
28374     
28375             listeners : {
28376                 click : function ()
28377                 {
28378                     // remove
28379                     // undo does not work.
28380                      
28381                     var sn = tb.selectedNode;
28382                     
28383                     var pn = sn.parentNode;
28384                     
28385                     var stn =  sn.childNodes[0];
28386                     var en = sn.childNodes[sn.childNodes.length - 1 ];
28387                     while (sn.childNodes.length) {
28388                         var node = sn.childNodes[0];
28389                         sn.removeChild(node);
28390                         //Roo.log(node);
28391                         pn.insertBefore(node, sn);
28392                         
28393                     }
28394                     pn.removeChild(sn);
28395                     var range = editorcore.createRange();
28396         
28397                     range.setStart(stn,0);
28398                     range.setEnd(en,0); //????
28399                     //range.selectNode(sel);
28400                     
28401                     
28402                     var selection = editorcore.getSelection();
28403                     selection.removeAllRanges();
28404                     selection.addRange(range);
28405                     
28406                     
28407                     
28408                     //_this.updateToolbar(null, null, pn);
28409                     _this.updateToolbar(null, null, null);
28410                     _this.footDisp.dom.innerHTML = ''; 
28411                 }
28412             }
28413             
28414                     
28415                 
28416             
28417         });
28418         
28419         
28420         tb.el.on('click', function(e){
28421             e.preventDefault(); // what does this do?
28422         });
28423         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
28424         tb.el.hide();
28425         tb.name = nm;
28426         // dont need to disable them... as they will get hidden
28427         return tb;
28428          
28429         
28430     },
28431     buildFooter : function()
28432     {
28433         
28434         var fel = this.editor.wrap.createChild();
28435         this.footer = new Roo.Toolbar(fel);
28436         // toolbar has scrolly on left / right?
28437         var footDisp= new Roo.Toolbar.Fill();
28438         var _t = this;
28439         this.footer.add(
28440             {
28441                 text : '&lt;',
28442                 xtype: 'Button',
28443                 handler : function() {
28444                     _t.footDisp.scrollTo('left',0,true)
28445                 }
28446             }
28447         );
28448         this.footer.add( footDisp );
28449         this.footer.add( 
28450             {
28451                 text : '&gt;',
28452                 xtype: 'Button',
28453                 handler : function() {
28454                     // no animation..
28455                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
28456                 }
28457             }
28458         );
28459         var fel = Roo.get(footDisp.el);
28460         fel.addClass('x-editor-context');
28461         this.footDispWrap = fel; 
28462         this.footDispWrap.overflow  = 'hidden';
28463         
28464         this.footDisp = fel.createChild();
28465         this.footDispWrap.on('click', this.onContextClick, this)
28466         
28467         
28468     },
28469     onContextClick : function (ev,dom)
28470     {
28471         ev.preventDefault();
28472         var  cn = dom.className;
28473         //Roo.log(cn);
28474         if (!cn.match(/x-ed-loc-/)) {
28475             return;
28476         }
28477         var n = cn.split('-').pop();
28478         var ans = this.footerEls;
28479         var sel = ans[n];
28480         
28481          // pick
28482         var range = this.editorcore.createRange();
28483         
28484         range.selectNodeContents(sel);
28485         //range.selectNode(sel);
28486         
28487         
28488         var selection = this.editorcore.getSelection();
28489         selection.removeAllRanges();
28490         selection.addRange(range);
28491         
28492         
28493         
28494         this.updateToolbar(null, null, sel);
28495         
28496         
28497     }
28498     
28499     
28500     
28501     
28502     
28503 });
28504
28505
28506
28507
28508
28509 /*
28510  * Based on:
28511  * Ext JS Library 1.1.1
28512  * Copyright(c) 2006-2007, Ext JS, LLC.
28513  *
28514  * Originally Released Under LGPL - original licence link has changed is not relivant.
28515  *
28516  * Fork - LGPL
28517  * <script type="text/javascript">
28518  */
28519  
28520 /**
28521  * @class Roo.form.BasicForm
28522  * @extends Roo.util.Observable
28523  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
28524  * @constructor
28525  * @param {String/HTMLElement/Roo.Element} el The form element or its id
28526  * @param {Object} config Configuration options
28527  */
28528 Roo.form.BasicForm = function(el, config){
28529     this.allItems = [];
28530     this.childForms = [];
28531     Roo.apply(this, config);
28532     /*
28533      * The Roo.form.Field items in this form.
28534      * @type MixedCollection
28535      */
28536      
28537      
28538     this.items = new Roo.util.MixedCollection(false, function(o){
28539         return o.id || (o.id = Roo.id());
28540     });
28541     this.addEvents({
28542         /**
28543          * @event beforeaction
28544          * Fires before any action is performed. Return false to cancel the action.
28545          * @param {Form} this
28546          * @param {Action} action The action to be performed
28547          */
28548         beforeaction: true,
28549         /**
28550          * @event actionfailed
28551          * Fires when an action fails.
28552          * @param {Form} this
28553          * @param {Action} action The action that failed
28554          */
28555         actionfailed : true,
28556         /**
28557          * @event actioncomplete
28558          * Fires when an action is completed.
28559          * @param {Form} this
28560          * @param {Action} action The action that completed
28561          */
28562         actioncomplete : true
28563     });
28564     if(el){
28565         this.initEl(el);
28566     }
28567     Roo.form.BasicForm.superclass.constructor.call(this);
28568 };
28569
28570 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
28571     /**
28572      * @cfg {String} method
28573      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
28574      */
28575     /**
28576      * @cfg {DataReader} reader
28577      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
28578      * This is optional as there is built-in support for processing JSON.
28579      */
28580     /**
28581      * @cfg {DataReader} errorReader
28582      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
28583      * This is completely optional as there is built-in support for processing JSON.
28584      */
28585     /**
28586      * @cfg {String} url
28587      * The URL to use for form actions if one isn't supplied in the action options.
28588      */
28589     /**
28590      * @cfg {Boolean} fileUpload
28591      * Set to true if this form is a file upload.
28592      */
28593      
28594     /**
28595      * @cfg {Object} baseParams
28596      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
28597      */
28598      /**
28599      
28600     /**
28601      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
28602      */
28603     timeout: 30,
28604
28605     // private
28606     activeAction : null,
28607
28608     /**
28609      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
28610      * or setValues() data instead of when the form was first created.
28611      */
28612     trackResetOnLoad : false,
28613     
28614     
28615     /**
28616      * childForms - used for multi-tab forms
28617      * @type {Array}
28618      */
28619     childForms : false,
28620     
28621     /**
28622      * allItems - full list of fields.
28623      * @type {Array}
28624      */
28625     allItems : false,
28626     
28627     /**
28628      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
28629      * element by passing it or its id or mask the form itself by passing in true.
28630      * @type Mixed
28631      */
28632     waitMsgTarget : false,
28633
28634     // private
28635     initEl : function(el){
28636         this.el = Roo.get(el);
28637         this.id = this.el.id || Roo.id();
28638         this.el.on('submit', this.onSubmit, this);
28639         this.el.addClass('x-form');
28640     },
28641
28642     // private
28643     onSubmit : function(e){
28644         e.stopEvent();
28645     },
28646
28647     /**
28648      * Returns true if client-side validation on the form is successful.
28649      * @return Boolean
28650      */
28651     isValid : function(){
28652         var valid = true;
28653         this.items.each(function(f){
28654            if(!f.validate()){
28655                valid = false;
28656            }
28657         });
28658         return valid;
28659     },
28660
28661     /**
28662      * Returns true if any fields in this form have changed since their original load.
28663      * @return Boolean
28664      */
28665     isDirty : function(){
28666         var dirty = false;
28667         this.items.each(function(f){
28668            if(f.isDirty()){
28669                dirty = true;
28670                return false;
28671            }
28672         });
28673         return dirty;
28674     },
28675
28676     /**
28677      * Performs a predefined action (submit or load) or custom actions you define on this form.
28678      * @param {String} actionName The name of the action type
28679      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
28680      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
28681      * accept other config options):
28682      * <pre>
28683 Property          Type             Description
28684 ----------------  ---------------  ----------------------------------------------------------------------------------
28685 url               String           The url for the action (defaults to the form's url)
28686 method            String           The form method to use (defaults to the form's method, or POST if not defined)
28687 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
28688 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
28689                                    validate the form on the client (defaults to false)
28690      * </pre>
28691      * @return {BasicForm} this
28692      */
28693     doAction : function(action, options){
28694         if(typeof action == 'string'){
28695             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
28696         }
28697         if(this.fireEvent('beforeaction', this, action) !== false){
28698             this.beforeAction(action);
28699             action.run.defer(100, action);
28700         }
28701         return this;
28702     },
28703
28704     /**
28705      * Shortcut to do a submit action.
28706      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28707      * @return {BasicForm} this
28708      */
28709     submit : function(options){
28710         this.doAction('submit', options);
28711         return this;
28712     },
28713
28714     /**
28715      * Shortcut to do a load action.
28716      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28717      * @return {BasicForm} this
28718      */
28719     load : function(options){
28720         this.doAction('load', options);
28721         return this;
28722     },
28723
28724     /**
28725      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
28726      * @param {Record} record The record to edit
28727      * @return {BasicForm} this
28728      */
28729     updateRecord : function(record){
28730         record.beginEdit();
28731         var fs = record.fields;
28732         fs.each(function(f){
28733             var field = this.findField(f.name);
28734             if(field){
28735                 record.set(f.name, field.getValue());
28736             }
28737         }, this);
28738         record.endEdit();
28739         return this;
28740     },
28741
28742     /**
28743      * Loads an Roo.data.Record into this form.
28744      * @param {Record} record The record to load
28745      * @return {BasicForm} this
28746      */
28747     loadRecord : function(record){
28748         this.setValues(record.data);
28749         return this;
28750     },
28751
28752     // private
28753     beforeAction : function(action){
28754         var o = action.options;
28755         
28756        
28757         if(this.waitMsgTarget === true){
28758             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
28759         }else if(this.waitMsgTarget){
28760             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
28761             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
28762         }else {
28763             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
28764         }
28765          
28766     },
28767
28768     // private
28769     afterAction : function(action, success){
28770         this.activeAction = null;
28771         var o = action.options;
28772         
28773         if(this.waitMsgTarget === true){
28774             this.el.unmask();
28775         }else if(this.waitMsgTarget){
28776             this.waitMsgTarget.unmask();
28777         }else{
28778             Roo.MessageBox.updateProgress(1);
28779             Roo.MessageBox.hide();
28780         }
28781          
28782         if(success){
28783             if(o.reset){
28784                 this.reset();
28785             }
28786             Roo.callback(o.success, o.scope, [this, action]);
28787             this.fireEvent('actioncomplete', this, action);
28788             
28789         }else{
28790             
28791             // failure condition..
28792             // we have a scenario where updates need confirming.
28793             // eg. if a locking scenario exists..
28794             // we look for { errors : { needs_confirm : true }} in the response.
28795             if (
28796                 (typeof(action.result) != 'undefined')  &&
28797                 (typeof(action.result.errors) != 'undefined')  &&
28798                 (typeof(action.result.errors.needs_confirm) != 'undefined')
28799            ){
28800                 var _t = this;
28801                 Roo.MessageBox.confirm(
28802                     "Change requires confirmation",
28803                     action.result.errorMsg,
28804                     function(r) {
28805                         if (r != 'yes') {
28806                             return;
28807                         }
28808                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
28809                     }
28810                     
28811                 );
28812                 
28813                 
28814                 
28815                 return;
28816             }
28817             
28818             Roo.callback(o.failure, o.scope, [this, action]);
28819             // show an error message if no failed handler is set..
28820             if (!this.hasListener('actionfailed')) {
28821                 Roo.MessageBox.alert("Error",
28822                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
28823                         action.result.errorMsg :
28824                         "Saving Failed, please check your entries or try again"
28825                 );
28826             }
28827             
28828             this.fireEvent('actionfailed', this, action);
28829         }
28830         
28831     },
28832
28833     /**
28834      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
28835      * @param {String} id The value to search for
28836      * @return Field
28837      */
28838     findField : function(id){
28839         var field = this.items.get(id);
28840         if(!field){
28841             this.items.each(function(f){
28842                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
28843                     field = f;
28844                     return false;
28845                 }
28846             });
28847         }
28848         return field || null;
28849     },
28850
28851     /**
28852      * Add a secondary form to this one, 
28853      * Used to provide tabbed forms. One form is primary, with hidden values 
28854      * which mirror the elements from the other forms.
28855      * 
28856      * @param {Roo.form.Form} form to add.
28857      * 
28858      */
28859     addForm : function(form)
28860     {
28861        
28862         if (this.childForms.indexOf(form) > -1) {
28863             // already added..
28864             return;
28865         }
28866         this.childForms.push(form);
28867         var n = '';
28868         Roo.each(form.allItems, function (fe) {
28869             
28870             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
28871             if (this.findField(n)) { // already added..
28872                 return;
28873             }
28874             var add = new Roo.form.Hidden({
28875                 name : n
28876             });
28877             add.render(this.el);
28878             
28879             this.add( add );
28880         }, this);
28881         
28882     },
28883     /**
28884      * Mark fields in this form invalid in bulk.
28885      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
28886      * @return {BasicForm} this
28887      */
28888     markInvalid : function(errors){
28889         if(errors instanceof Array){
28890             for(var i = 0, len = errors.length; i < len; i++){
28891                 var fieldError = errors[i];
28892                 var f = this.findField(fieldError.id);
28893                 if(f){
28894                     f.markInvalid(fieldError.msg);
28895                 }
28896             }
28897         }else{
28898             var field, id;
28899             for(id in errors){
28900                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
28901                     field.markInvalid(errors[id]);
28902                 }
28903             }
28904         }
28905         Roo.each(this.childForms || [], function (f) {
28906             f.markInvalid(errors);
28907         });
28908         
28909         return this;
28910     },
28911
28912     /**
28913      * Set values for fields in this form in bulk.
28914      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
28915      * @return {BasicForm} this
28916      */
28917     setValues : function(values){
28918         if(values instanceof Array){ // array of objects
28919             for(var i = 0, len = values.length; i < len; i++){
28920                 var v = values[i];
28921                 var f = this.findField(v.id);
28922                 if(f){
28923                     f.setValue(v.value);
28924                     if(this.trackResetOnLoad){
28925                         f.originalValue = f.getValue();
28926                     }
28927                 }
28928             }
28929         }else{ // object hash
28930             var field, id;
28931             for(id in values){
28932                 if(typeof values[id] != 'function' && (field = this.findField(id))){
28933                     
28934                     if (field.setFromData && 
28935                         field.valueField && 
28936                         field.displayField &&
28937                         // combos' with local stores can 
28938                         // be queried via setValue()
28939                         // to set their value..
28940                         (field.store && !field.store.isLocal)
28941                         ) {
28942                         // it's a combo
28943                         var sd = { };
28944                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
28945                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
28946                         field.setFromData(sd);
28947                         
28948                     } else {
28949                         field.setValue(values[id]);
28950                     }
28951                     
28952                     
28953                     if(this.trackResetOnLoad){
28954                         field.originalValue = field.getValue();
28955                     }
28956                 }
28957             }
28958         }
28959          
28960         Roo.each(this.childForms || [], function (f) {
28961             f.setValues(values);
28962         });
28963                 
28964         return this;
28965     },
28966
28967     /**
28968      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
28969      * they are returned as an array.
28970      * @param {Boolean} asString
28971      * @return {Object}
28972      */
28973     getValues : function(asString){
28974         if (this.childForms) {
28975             // copy values from the child forms
28976             Roo.each(this.childForms, function (f) {
28977                 this.setValues(f.getValues());
28978             }, this);
28979         }
28980         
28981         
28982         
28983         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
28984         if(asString === true){
28985             return fs;
28986         }
28987         return Roo.urlDecode(fs);
28988     },
28989     
28990     /**
28991      * Returns the fields in this form as an object with key/value pairs. 
28992      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
28993      * @return {Object}
28994      */
28995     getFieldValues : function(with_hidden)
28996     {
28997         if (this.childForms) {
28998             // copy values from the child forms
28999             // should this call getFieldValues - probably not as we do not currently copy
29000             // hidden fields when we generate..
29001             Roo.each(this.childForms, function (f) {
29002                 this.setValues(f.getValues());
29003             }, this);
29004         }
29005         
29006         var ret = {};
29007         this.items.each(function(f){
29008             if (!f.getName()) {
29009                 return;
29010             }
29011             var v = f.getValue();
29012             if (f.inputType =='radio') {
29013                 if (typeof(ret[f.getName()]) == 'undefined') {
29014                     ret[f.getName()] = ''; // empty..
29015                 }
29016                 
29017                 if (!f.el.dom.checked) {
29018                     return;
29019                     
29020                 }
29021                 v = f.el.dom.value;
29022                 
29023             }
29024             
29025             // not sure if this supported any more..
29026             if ((typeof(v) == 'object') && f.getRawValue) {
29027                 v = f.getRawValue() ; // dates..
29028             }
29029             // combo boxes where name != hiddenName...
29030             if (f.name != f.getName()) {
29031                 ret[f.name] = f.getRawValue();
29032             }
29033             ret[f.getName()] = v;
29034         });
29035         
29036         return ret;
29037     },
29038
29039     /**
29040      * Clears all invalid messages in this form.
29041      * @return {BasicForm} this
29042      */
29043     clearInvalid : function(){
29044         this.items.each(function(f){
29045            f.clearInvalid();
29046         });
29047         
29048         Roo.each(this.childForms || [], function (f) {
29049             f.clearInvalid();
29050         });
29051         
29052         
29053         return this;
29054     },
29055
29056     /**
29057      * Resets this form.
29058      * @return {BasicForm} this
29059      */
29060     reset : function(){
29061         this.items.each(function(f){
29062             f.reset();
29063         });
29064         
29065         Roo.each(this.childForms || [], function (f) {
29066             f.reset();
29067         });
29068        
29069         
29070         return this;
29071     },
29072
29073     /**
29074      * Add Roo.form components to this form.
29075      * @param {Field} field1
29076      * @param {Field} field2 (optional)
29077      * @param {Field} etc (optional)
29078      * @return {BasicForm} this
29079      */
29080     add : function(){
29081         this.items.addAll(Array.prototype.slice.call(arguments, 0));
29082         return this;
29083     },
29084
29085
29086     /**
29087      * Removes a field from the items collection (does NOT remove its markup).
29088      * @param {Field} field
29089      * @return {BasicForm} this
29090      */
29091     remove : function(field){
29092         this.items.remove(field);
29093         return this;
29094     },
29095
29096     /**
29097      * Looks at the fields in this form, checks them for an id attribute,
29098      * and calls applyTo on the existing dom element with that id.
29099      * @return {BasicForm} this
29100      */
29101     render : function(){
29102         this.items.each(function(f){
29103             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
29104                 f.applyTo(f.id);
29105             }
29106         });
29107         return this;
29108     },
29109
29110     /**
29111      * Calls {@link Ext#apply} for all fields in this form with the passed object.
29112      * @param {Object} values
29113      * @return {BasicForm} this
29114      */
29115     applyToFields : function(o){
29116         this.items.each(function(f){
29117            Roo.apply(f, o);
29118         });
29119         return this;
29120     },
29121
29122     /**
29123      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
29124      * @param {Object} values
29125      * @return {BasicForm} this
29126      */
29127     applyIfToFields : function(o){
29128         this.items.each(function(f){
29129            Roo.applyIf(f, o);
29130         });
29131         return this;
29132     }
29133 });
29134
29135 // back compat
29136 Roo.BasicForm = Roo.form.BasicForm;/*
29137  * Based on:
29138  * Ext JS Library 1.1.1
29139  * Copyright(c) 2006-2007, Ext JS, LLC.
29140  *
29141  * Originally Released Under LGPL - original licence link has changed is not relivant.
29142  *
29143  * Fork - LGPL
29144  * <script type="text/javascript">
29145  */
29146
29147 /**
29148  * @class Roo.form.Form
29149  * @extends Roo.form.BasicForm
29150  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
29151  * @constructor
29152  * @param {Object} config Configuration options
29153  */
29154 Roo.form.Form = function(config){
29155     var xitems =  [];
29156     if (config.items) {
29157         xitems = config.items;
29158         delete config.items;
29159     }
29160    
29161     
29162     Roo.form.Form.superclass.constructor.call(this, null, config);
29163     this.url = this.url || this.action;
29164     if(!this.root){
29165         this.root = new Roo.form.Layout(Roo.applyIf({
29166             id: Roo.id()
29167         }, config));
29168     }
29169     this.active = this.root;
29170     /**
29171      * Array of all the buttons that have been added to this form via {@link addButton}
29172      * @type Array
29173      */
29174     this.buttons = [];
29175     this.allItems = [];
29176     this.addEvents({
29177         /**
29178          * @event clientvalidation
29179          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
29180          * @param {Form} this
29181          * @param {Boolean} valid true if the form has passed client-side validation
29182          */
29183         clientvalidation: true,
29184         /**
29185          * @event rendered
29186          * Fires when the form is rendered
29187          * @param {Roo.form.Form} form
29188          */
29189         rendered : true
29190     });
29191     
29192     if (this.progressUrl) {
29193             // push a hidden field onto the list of fields..
29194             this.addxtype( {
29195                     xns: Roo.form, 
29196                     xtype : 'Hidden', 
29197                     name : 'UPLOAD_IDENTIFIER' 
29198             });
29199         }
29200         
29201     
29202     Roo.each(xitems, this.addxtype, this);
29203     
29204     
29205     
29206 };
29207
29208 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
29209     /**
29210      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
29211      */
29212     /**
29213      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
29214      */
29215     /**
29216      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
29217      */
29218     buttonAlign:'center',
29219
29220     /**
29221      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
29222      */
29223     minButtonWidth:75,
29224
29225     /**
29226      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
29227      * This property cascades to child containers if not set.
29228      */
29229     labelAlign:'left',
29230
29231     /**
29232      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
29233      * fires a looping event with that state. This is required to bind buttons to the valid
29234      * state using the config value formBind:true on the button.
29235      */
29236     monitorValid : false,
29237
29238     /**
29239      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
29240      */
29241     monitorPoll : 200,
29242     
29243     /**
29244      * @cfg {String} progressUrl - Url to return progress data 
29245      */
29246     
29247     progressUrl : false,
29248   
29249     /**
29250      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
29251      * fields are added and the column is closed. If no fields are passed the column remains open
29252      * until end() is called.
29253      * @param {Object} config The config to pass to the column
29254      * @param {Field} field1 (optional)
29255      * @param {Field} field2 (optional)
29256      * @param {Field} etc (optional)
29257      * @return Column The column container object
29258      */
29259     column : function(c){
29260         var col = new Roo.form.Column(c);
29261         this.start(col);
29262         if(arguments.length > 1){ // duplicate code required because of Opera
29263             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29264             this.end();
29265         }
29266         return col;
29267     },
29268
29269     /**
29270      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
29271      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
29272      * until end() is called.
29273      * @param {Object} config The config to pass to the fieldset
29274      * @param {Field} field1 (optional)
29275      * @param {Field} field2 (optional)
29276      * @param {Field} etc (optional)
29277      * @return FieldSet The fieldset container object
29278      */
29279     fieldset : function(c){
29280         var fs = new Roo.form.FieldSet(c);
29281         this.start(fs);
29282         if(arguments.length > 1){ // duplicate code required because of Opera
29283             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29284             this.end();
29285         }
29286         return fs;
29287     },
29288
29289     /**
29290      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
29291      * fields are added and the container is closed. If no fields are passed the container remains open
29292      * until end() is called.
29293      * @param {Object} config The config to pass to the Layout
29294      * @param {Field} field1 (optional)
29295      * @param {Field} field2 (optional)
29296      * @param {Field} etc (optional)
29297      * @return Layout The container object
29298      */
29299     container : function(c){
29300         var l = new Roo.form.Layout(c);
29301         this.start(l);
29302         if(arguments.length > 1){ // duplicate code required because of Opera
29303             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29304             this.end();
29305         }
29306         return l;
29307     },
29308
29309     /**
29310      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
29311      * @param {Object} container A Roo.form.Layout or subclass of Layout
29312      * @return {Form} this
29313      */
29314     start : function(c){
29315         // cascade label info
29316         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
29317         this.active.stack.push(c);
29318         c.ownerCt = this.active;
29319         this.active = c;
29320         return this;
29321     },
29322
29323     /**
29324      * Closes the current open container
29325      * @return {Form} this
29326      */
29327     end : function(){
29328         if(this.active == this.root){
29329             return this;
29330         }
29331         this.active = this.active.ownerCt;
29332         return this;
29333     },
29334
29335     /**
29336      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
29337      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
29338      * as the label of the field.
29339      * @param {Field} field1
29340      * @param {Field} field2 (optional)
29341      * @param {Field} etc. (optional)
29342      * @return {Form} this
29343      */
29344     add : function(){
29345         this.active.stack.push.apply(this.active.stack, arguments);
29346         this.allItems.push.apply(this.allItems,arguments);
29347         var r = [];
29348         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
29349             if(a[i].isFormField){
29350                 r.push(a[i]);
29351             }
29352         }
29353         if(r.length > 0){
29354             Roo.form.Form.superclass.add.apply(this, r);
29355         }
29356         return this;
29357     },
29358     
29359
29360     
29361     
29362     
29363      /**
29364      * Find any element that has been added to a form, using it's ID or name
29365      * This can include framesets, columns etc. along with regular fields..
29366      * @param {String} id - id or name to find.
29367      
29368      * @return {Element} e - or false if nothing found.
29369      */
29370     findbyId : function(id)
29371     {
29372         var ret = false;
29373         if (!id) {
29374             return ret;
29375         }
29376         Roo.each(this.allItems, function(f){
29377             if (f.id == id || f.name == id ){
29378                 ret = f;
29379                 return false;
29380             }
29381         });
29382         return ret;
29383     },
29384
29385     
29386     
29387     /**
29388      * Render this form into the passed container. This should only be called once!
29389      * @param {String/HTMLElement/Element} container The element this component should be rendered into
29390      * @return {Form} this
29391      */
29392     render : function(ct)
29393     {
29394         
29395         
29396         
29397         ct = Roo.get(ct);
29398         var o = this.autoCreate || {
29399             tag: 'form',
29400             method : this.method || 'POST',
29401             id : this.id || Roo.id()
29402         };
29403         this.initEl(ct.createChild(o));
29404
29405         this.root.render(this.el);
29406         
29407        
29408              
29409         this.items.each(function(f){
29410             f.render('x-form-el-'+f.id);
29411         });
29412
29413         if(this.buttons.length > 0){
29414             // tables are required to maintain order and for correct IE layout
29415             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
29416                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
29417                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29418             }}, null, true);
29419             var tr = tb.getElementsByTagName('tr')[0];
29420             for(var i = 0, len = this.buttons.length; i < len; i++) {
29421                 var b = this.buttons[i];
29422                 var td = document.createElement('td');
29423                 td.className = 'x-form-btn-td';
29424                 b.render(tr.appendChild(td));
29425             }
29426         }
29427         if(this.monitorValid){ // initialize after render
29428             this.startMonitoring();
29429         }
29430         this.fireEvent('rendered', this);
29431         return this;
29432     },
29433
29434     /**
29435      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
29436      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29437      * object or a valid Roo.DomHelper element config
29438      * @param {Function} handler The function called when the button is clicked
29439      * @param {Object} scope (optional) The scope of the handler function
29440      * @return {Roo.Button}
29441      */
29442     addButton : function(config, handler, scope){
29443         var bc = {
29444             handler: handler,
29445             scope: scope,
29446             minWidth: this.minButtonWidth,
29447             hideParent:true
29448         };
29449         if(typeof config == "string"){
29450             bc.text = config;
29451         }else{
29452             Roo.apply(bc, config);
29453         }
29454         var btn = new Roo.Button(null, bc);
29455         this.buttons.push(btn);
29456         return btn;
29457     },
29458
29459      /**
29460      * Adds a series of form elements (using the xtype property as the factory method.
29461      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
29462      * @param {Object} config 
29463      */
29464     
29465     addxtype : function()
29466     {
29467         var ar = Array.prototype.slice.call(arguments, 0);
29468         var ret = false;
29469         for(var i = 0; i < ar.length; i++) {
29470             if (!ar[i]) {
29471                 continue; // skip -- if this happends something invalid got sent, we 
29472                 // should ignore it, as basically that interface element will not show up
29473                 // and that should be pretty obvious!!
29474             }
29475             
29476             if (Roo.form[ar[i].xtype]) {
29477                 ar[i].form = this;
29478                 var fe = Roo.factory(ar[i], Roo.form);
29479                 if (!ret) {
29480                     ret = fe;
29481                 }
29482                 fe.form = this;
29483                 if (fe.store) {
29484                     fe.store.form = this;
29485                 }
29486                 if (fe.isLayout) {  
29487                          
29488                     this.start(fe);
29489                     this.allItems.push(fe);
29490                     if (fe.items && fe.addxtype) {
29491                         fe.addxtype.apply(fe, fe.items);
29492                         delete fe.items;
29493                     }
29494                      this.end();
29495                     continue;
29496                 }
29497                 
29498                 
29499                  
29500                 this.add(fe);
29501               //  console.log('adding ' + ar[i].xtype);
29502             }
29503             if (ar[i].xtype == 'Button') {  
29504                 //console.log('adding button');
29505                 //console.log(ar[i]);
29506                 this.addButton(ar[i]);
29507                 this.allItems.push(fe);
29508                 continue;
29509             }
29510             
29511             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
29512                 alert('end is not supported on xtype any more, use items');
29513             //    this.end();
29514             //    //console.log('adding end');
29515             }
29516             
29517         }
29518         return ret;
29519     },
29520     
29521     /**
29522      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
29523      * option "monitorValid"
29524      */
29525     startMonitoring : function(){
29526         if(!this.bound){
29527             this.bound = true;
29528             Roo.TaskMgr.start({
29529                 run : this.bindHandler,
29530                 interval : this.monitorPoll || 200,
29531                 scope: this
29532             });
29533         }
29534     },
29535
29536     /**
29537      * Stops monitoring of the valid state of this form
29538      */
29539     stopMonitoring : function(){
29540         this.bound = false;
29541     },
29542
29543     // private
29544     bindHandler : function(){
29545         if(!this.bound){
29546             return false; // stops binding
29547         }
29548         var valid = true;
29549         this.items.each(function(f){
29550             if(!f.isValid(true)){
29551                 valid = false;
29552                 return false;
29553             }
29554         });
29555         for(var i = 0, len = this.buttons.length; i < len; i++){
29556             var btn = this.buttons[i];
29557             if(btn.formBind === true && btn.disabled === valid){
29558                 btn.setDisabled(!valid);
29559             }
29560         }
29561         this.fireEvent('clientvalidation', this, valid);
29562     }
29563     
29564     
29565     
29566     
29567     
29568     
29569     
29570     
29571 });
29572
29573
29574 // back compat
29575 Roo.Form = Roo.form.Form;
29576 /*
29577  * Based on:
29578  * Ext JS Library 1.1.1
29579  * Copyright(c) 2006-2007, Ext JS, LLC.
29580  *
29581  * Originally Released Under LGPL - original licence link has changed is not relivant.
29582  *
29583  * Fork - LGPL
29584  * <script type="text/javascript">
29585  */
29586
29587 // as we use this in bootstrap.
29588 Roo.namespace('Roo.form');
29589  /**
29590  * @class Roo.form.Action
29591  * Internal Class used to handle form actions
29592  * @constructor
29593  * @param {Roo.form.BasicForm} el The form element or its id
29594  * @param {Object} config Configuration options
29595  */
29596
29597  
29598  
29599 // define the action interface
29600 Roo.form.Action = function(form, options){
29601     this.form = form;
29602     this.options = options || {};
29603 };
29604 /**
29605  * Client Validation Failed
29606  * @const 
29607  */
29608 Roo.form.Action.CLIENT_INVALID = 'client';
29609 /**
29610  * Server Validation Failed
29611  * @const 
29612  */
29613 Roo.form.Action.SERVER_INVALID = 'server';
29614  /**
29615  * Connect to Server Failed
29616  * @const 
29617  */
29618 Roo.form.Action.CONNECT_FAILURE = 'connect';
29619 /**
29620  * Reading Data from Server Failed
29621  * @const 
29622  */
29623 Roo.form.Action.LOAD_FAILURE = 'load';
29624
29625 Roo.form.Action.prototype = {
29626     type : 'default',
29627     failureType : undefined,
29628     response : undefined,
29629     result : undefined,
29630
29631     // interface method
29632     run : function(options){
29633
29634     },
29635
29636     // interface method
29637     success : function(response){
29638
29639     },
29640
29641     // interface method
29642     handleResponse : function(response){
29643
29644     },
29645
29646     // default connection failure
29647     failure : function(response){
29648         
29649         this.response = response;
29650         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29651         this.form.afterAction(this, false);
29652     },
29653
29654     processResponse : function(response){
29655         this.response = response;
29656         if(!response.responseText){
29657             return true;
29658         }
29659         this.result = this.handleResponse(response);
29660         return this.result;
29661     },
29662
29663     // utility functions used internally
29664     getUrl : function(appendParams){
29665         var url = this.options.url || this.form.url || this.form.el.dom.action;
29666         if(appendParams){
29667             var p = this.getParams();
29668             if(p){
29669                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
29670             }
29671         }
29672         return url;
29673     },
29674
29675     getMethod : function(){
29676         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
29677     },
29678
29679     getParams : function(){
29680         var bp = this.form.baseParams;
29681         var p = this.options.params;
29682         if(p){
29683             if(typeof p == "object"){
29684                 p = Roo.urlEncode(Roo.applyIf(p, bp));
29685             }else if(typeof p == 'string' && bp){
29686                 p += '&' + Roo.urlEncode(bp);
29687             }
29688         }else if(bp){
29689             p = Roo.urlEncode(bp);
29690         }
29691         return p;
29692     },
29693
29694     createCallback : function(){
29695         return {
29696             success: this.success,
29697             failure: this.failure,
29698             scope: this,
29699             timeout: (this.form.timeout*1000),
29700             upload: this.form.fileUpload ? this.success : undefined
29701         };
29702     }
29703 };
29704
29705 Roo.form.Action.Submit = function(form, options){
29706     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
29707 };
29708
29709 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
29710     type : 'submit',
29711
29712     haveProgress : false,
29713     uploadComplete : false,
29714     
29715     // uploadProgress indicator.
29716     uploadProgress : function()
29717     {
29718         if (!this.form.progressUrl) {
29719             return;
29720         }
29721         
29722         if (!this.haveProgress) {
29723             Roo.MessageBox.progress("Uploading", "Uploading");
29724         }
29725         if (this.uploadComplete) {
29726            Roo.MessageBox.hide();
29727            return;
29728         }
29729         
29730         this.haveProgress = true;
29731    
29732         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
29733         
29734         var c = new Roo.data.Connection();
29735         c.request({
29736             url : this.form.progressUrl,
29737             params: {
29738                 id : uid
29739             },
29740             method: 'GET',
29741             success : function(req){
29742                //console.log(data);
29743                 var rdata = false;
29744                 var edata;
29745                 try  {
29746                    rdata = Roo.decode(req.responseText)
29747                 } catch (e) {
29748                     Roo.log("Invalid data from server..");
29749                     Roo.log(edata);
29750                     return;
29751                 }
29752                 if (!rdata || !rdata.success) {
29753                     Roo.log(rdata);
29754                     Roo.MessageBox.alert(Roo.encode(rdata));
29755                     return;
29756                 }
29757                 var data = rdata.data;
29758                 
29759                 if (this.uploadComplete) {
29760                    Roo.MessageBox.hide();
29761                    return;
29762                 }
29763                    
29764                 if (data){
29765                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
29766                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
29767                     );
29768                 }
29769                 this.uploadProgress.defer(2000,this);
29770             },
29771        
29772             failure: function(data) {
29773                 Roo.log('progress url failed ');
29774                 Roo.log(data);
29775             },
29776             scope : this
29777         });
29778            
29779     },
29780     
29781     
29782     run : function()
29783     {
29784         // run get Values on the form, so it syncs any secondary forms.
29785         this.form.getValues();
29786         
29787         var o = this.options;
29788         var method = this.getMethod();
29789         var isPost = method == 'POST';
29790         if(o.clientValidation === false || this.form.isValid()){
29791             
29792             if (this.form.progressUrl) {
29793                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
29794                     (new Date() * 1) + '' + Math.random());
29795                     
29796             } 
29797             
29798             
29799             Roo.Ajax.request(Roo.apply(this.createCallback(), {
29800                 form:this.form.el.dom,
29801                 url:this.getUrl(!isPost),
29802                 method: method,
29803                 params:isPost ? this.getParams() : null,
29804                 isUpload: this.form.fileUpload
29805             }));
29806             
29807             this.uploadProgress();
29808
29809         }else if (o.clientValidation !== false){ // client validation failed
29810             this.failureType = Roo.form.Action.CLIENT_INVALID;
29811             this.form.afterAction(this, false);
29812         }
29813     },
29814
29815     success : function(response)
29816     {
29817         this.uploadComplete= true;
29818         if (this.haveProgress) {
29819             Roo.MessageBox.hide();
29820         }
29821         
29822         
29823         var result = this.processResponse(response);
29824         if(result === true || result.success){
29825             this.form.afterAction(this, true);
29826             return;
29827         }
29828         if(result.errors){
29829             this.form.markInvalid(result.errors);
29830             this.failureType = Roo.form.Action.SERVER_INVALID;
29831         }
29832         this.form.afterAction(this, false);
29833     },
29834     failure : function(response)
29835     {
29836         this.uploadComplete= true;
29837         if (this.haveProgress) {
29838             Roo.MessageBox.hide();
29839         }
29840         
29841         this.response = response;
29842         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29843         this.form.afterAction(this, false);
29844     },
29845     
29846     handleResponse : function(response){
29847         if(this.form.errorReader){
29848             var rs = this.form.errorReader.read(response);
29849             var errors = [];
29850             if(rs.records){
29851                 for(var i = 0, len = rs.records.length; i < len; i++) {
29852                     var r = rs.records[i];
29853                     errors[i] = r.data;
29854                 }
29855             }
29856             if(errors.length < 1){
29857                 errors = null;
29858             }
29859             return {
29860                 success : rs.success,
29861                 errors : errors
29862             };
29863         }
29864         var ret = false;
29865         try {
29866             ret = Roo.decode(response.responseText);
29867         } catch (e) {
29868             ret = {
29869                 success: false,
29870                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
29871                 errors : []
29872             };
29873         }
29874         return ret;
29875         
29876     }
29877 });
29878
29879
29880 Roo.form.Action.Load = function(form, options){
29881     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
29882     this.reader = this.form.reader;
29883 };
29884
29885 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
29886     type : 'load',
29887
29888     run : function(){
29889         
29890         Roo.Ajax.request(Roo.apply(
29891                 this.createCallback(), {
29892                     method:this.getMethod(),
29893                     url:this.getUrl(false),
29894                     params:this.getParams()
29895         }));
29896     },
29897
29898     success : function(response){
29899         
29900         var result = this.processResponse(response);
29901         if(result === true || !result.success || !result.data){
29902             this.failureType = Roo.form.Action.LOAD_FAILURE;
29903             this.form.afterAction(this, false);
29904             return;
29905         }
29906         this.form.clearInvalid();
29907         this.form.setValues(result.data);
29908         this.form.afterAction(this, true);
29909     },
29910
29911     handleResponse : function(response){
29912         if(this.form.reader){
29913             var rs = this.form.reader.read(response);
29914             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
29915             return {
29916                 success : rs.success,
29917                 data : data
29918             };
29919         }
29920         return Roo.decode(response.responseText);
29921     }
29922 });
29923
29924 Roo.form.Action.ACTION_TYPES = {
29925     'load' : Roo.form.Action.Load,
29926     'submit' : Roo.form.Action.Submit
29927 };/*
29928  * Based on:
29929  * Ext JS Library 1.1.1
29930  * Copyright(c) 2006-2007, Ext JS, LLC.
29931  *
29932  * Originally Released Under LGPL - original licence link has changed is not relivant.
29933  *
29934  * Fork - LGPL
29935  * <script type="text/javascript">
29936  */
29937  
29938 /**
29939  * @class Roo.form.Layout
29940  * @extends Roo.Component
29941  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
29942  * @constructor
29943  * @param {Object} config Configuration options
29944  */
29945 Roo.form.Layout = function(config){
29946     var xitems = [];
29947     if (config.items) {
29948         xitems = config.items;
29949         delete config.items;
29950     }
29951     Roo.form.Layout.superclass.constructor.call(this, config);
29952     this.stack = [];
29953     Roo.each(xitems, this.addxtype, this);
29954      
29955 };
29956
29957 Roo.extend(Roo.form.Layout, Roo.Component, {
29958     /**
29959      * @cfg {String/Object} autoCreate
29960      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
29961      */
29962     /**
29963      * @cfg {String/Object/Function} style
29964      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
29965      * a function which returns such a specification.
29966      */
29967     /**
29968      * @cfg {String} labelAlign
29969      * Valid values are "left," "top" and "right" (defaults to "left")
29970      */
29971     /**
29972      * @cfg {Number} labelWidth
29973      * Fixed width in pixels of all field labels (defaults to undefined)
29974      */
29975     /**
29976      * @cfg {Boolean} clear
29977      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
29978      */
29979     clear : true,
29980     /**
29981      * @cfg {String} labelSeparator
29982      * The separator to use after field labels (defaults to ':')
29983      */
29984     labelSeparator : ':',
29985     /**
29986      * @cfg {Boolean} hideLabels
29987      * True to suppress the display of field labels in this layout (defaults to false)
29988      */
29989     hideLabels : false,
29990
29991     // private
29992     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
29993     
29994     isLayout : true,
29995     
29996     // private
29997     onRender : function(ct, position){
29998         if(this.el){ // from markup
29999             this.el = Roo.get(this.el);
30000         }else {  // generate
30001             var cfg = this.getAutoCreate();
30002             this.el = ct.createChild(cfg, position);
30003         }
30004         if(this.style){
30005             this.el.applyStyles(this.style);
30006         }
30007         if(this.labelAlign){
30008             this.el.addClass('x-form-label-'+this.labelAlign);
30009         }
30010         if(this.hideLabels){
30011             this.labelStyle = "display:none";
30012             this.elementStyle = "padding-left:0;";
30013         }else{
30014             if(typeof this.labelWidth == 'number'){
30015                 this.labelStyle = "width:"+this.labelWidth+"px;";
30016                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
30017             }
30018             if(this.labelAlign == 'top'){
30019                 this.labelStyle = "width:auto;";
30020                 this.elementStyle = "padding-left:0;";
30021             }
30022         }
30023         var stack = this.stack;
30024         var slen = stack.length;
30025         if(slen > 0){
30026             if(!this.fieldTpl){
30027                 var t = new Roo.Template(
30028                     '<div class="x-form-item {5}">',
30029                         '<label for="{0}" style="{2}">{1}{4}</label>',
30030                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30031                         '</div>',
30032                     '</div><div class="x-form-clear-left"></div>'
30033                 );
30034                 t.disableFormats = true;
30035                 t.compile();
30036                 Roo.form.Layout.prototype.fieldTpl = t;
30037             }
30038             for(var i = 0; i < slen; i++) {
30039                 if(stack[i].isFormField){
30040                     this.renderField(stack[i]);
30041                 }else{
30042                     this.renderComponent(stack[i]);
30043                 }
30044             }
30045         }
30046         if(this.clear){
30047             this.el.createChild({cls:'x-form-clear'});
30048         }
30049     },
30050
30051     // private
30052     renderField : function(f){
30053         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
30054                f.id, //0
30055                f.fieldLabel, //1
30056                f.labelStyle||this.labelStyle||'', //2
30057                this.elementStyle||'', //3
30058                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
30059                f.itemCls||this.itemCls||''  //5
30060        ], true).getPrevSibling());
30061     },
30062
30063     // private
30064     renderComponent : function(c){
30065         c.render(c.isLayout ? this.el : this.el.createChild());    
30066     },
30067     /**
30068      * Adds a object form elements (using the xtype property as the factory method.)
30069      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
30070      * @param {Object} config 
30071      */
30072     addxtype : function(o)
30073     {
30074         // create the lement.
30075         o.form = this.form;
30076         var fe = Roo.factory(o, Roo.form);
30077         this.form.allItems.push(fe);
30078         this.stack.push(fe);
30079         
30080         if (fe.isFormField) {
30081             this.form.items.add(fe);
30082         }
30083          
30084         return fe;
30085     }
30086 });
30087
30088 /**
30089  * @class Roo.form.Column
30090  * @extends Roo.form.Layout
30091  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
30092  * @constructor
30093  * @param {Object} config Configuration options
30094  */
30095 Roo.form.Column = function(config){
30096     Roo.form.Column.superclass.constructor.call(this, config);
30097 };
30098
30099 Roo.extend(Roo.form.Column, Roo.form.Layout, {
30100     /**
30101      * @cfg {Number/String} width
30102      * The fixed width of the column in pixels or CSS value (defaults to "auto")
30103      */
30104     /**
30105      * @cfg {String/Object} autoCreate
30106      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
30107      */
30108
30109     // private
30110     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
30111
30112     // private
30113     onRender : function(ct, position){
30114         Roo.form.Column.superclass.onRender.call(this, ct, position);
30115         if(this.width){
30116             this.el.setWidth(this.width);
30117         }
30118     }
30119 });
30120
30121
30122 /**
30123  * @class Roo.form.Row
30124  * @extends Roo.form.Layout
30125  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
30126  * @constructor
30127  * @param {Object} config Configuration options
30128  */
30129
30130  
30131 Roo.form.Row = function(config){
30132     Roo.form.Row.superclass.constructor.call(this, config);
30133 };
30134  
30135 Roo.extend(Roo.form.Row, Roo.form.Layout, {
30136       /**
30137      * @cfg {Number/String} width
30138      * The fixed width of the column in pixels or CSS value (defaults to "auto")
30139      */
30140     /**
30141      * @cfg {Number/String} height
30142      * The fixed height of the column in pixels or CSS value (defaults to "auto")
30143      */
30144     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
30145     
30146     padWidth : 20,
30147     // private
30148     onRender : function(ct, position){
30149         //console.log('row render');
30150         if(!this.rowTpl){
30151             var t = new Roo.Template(
30152                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
30153                     '<label for="{0}" style="{2}">{1}{4}</label>',
30154                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30155                     '</div>',
30156                 '</div>'
30157             );
30158             t.disableFormats = true;
30159             t.compile();
30160             Roo.form.Layout.prototype.rowTpl = t;
30161         }
30162         this.fieldTpl = this.rowTpl;
30163         
30164         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
30165         var labelWidth = 100;
30166         
30167         if ((this.labelAlign != 'top')) {
30168             if (typeof this.labelWidth == 'number') {
30169                 labelWidth = this.labelWidth
30170             }
30171             this.padWidth =  20 + labelWidth;
30172             
30173         }
30174         
30175         Roo.form.Column.superclass.onRender.call(this, ct, position);
30176         if(this.width){
30177             this.el.setWidth(this.width);
30178         }
30179         if(this.height){
30180             this.el.setHeight(this.height);
30181         }
30182     },
30183     
30184     // private
30185     renderField : function(f){
30186         f.fieldEl = this.fieldTpl.append(this.el, [
30187                f.id, f.fieldLabel,
30188                f.labelStyle||this.labelStyle||'',
30189                this.elementStyle||'',
30190                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
30191                f.itemCls||this.itemCls||'',
30192                f.width ? f.width + this.padWidth : 160 + this.padWidth
30193        ],true);
30194     }
30195 });
30196  
30197
30198 /**
30199  * @class Roo.form.FieldSet
30200  * @extends Roo.form.Layout
30201  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
30202  * @constructor
30203  * @param {Object} config Configuration options
30204  */
30205 Roo.form.FieldSet = function(config){
30206     Roo.form.FieldSet.superclass.constructor.call(this, config);
30207 };
30208
30209 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
30210     /**
30211      * @cfg {String} legend
30212      * The text to display as the legend for the FieldSet (defaults to '')
30213      */
30214     /**
30215      * @cfg {String/Object} autoCreate
30216      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
30217      */
30218
30219     // private
30220     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
30221
30222     // private
30223     onRender : function(ct, position){
30224         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
30225         if(this.legend){
30226             this.setLegend(this.legend);
30227         }
30228     },
30229
30230     // private
30231     setLegend : function(text){
30232         if(this.rendered){
30233             this.el.child('legend').update(text);
30234         }
30235     }
30236 });/*
30237  * Based on:
30238  * Ext JS Library 1.1.1
30239  * Copyright(c) 2006-2007, Ext JS, LLC.
30240  *
30241  * Originally Released Under LGPL - original licence link has changed is not relivant.
30242  *
30243  * Fork - LGPL
30244  * <script type="text/javascript">
30245  */
30246 /**
30247  * @class Roo.form.VTypes
30248  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
30249  * @singleton
30250  */
30251 Roo.form.VTypes = function(){
30252     // closure these in so they are only created once.
30253     var alpha = /^[a-zA-Z_]+$/;
30254     var alphanum = /^[a-zA-Z0-9_]+$/;
30255     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
30256     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
30257
30258     // All these messages and functions are configurable
30259     return {
30260         /**
30261          * The function used to validate email addresses
30262          * @param {String} value The email address
30263          */
30264         'email' : function(v){
30265             return email.test(v);
30266         },
30267         /**
30268          * The error text to display when the email validation function returns false
30269          * @type String
30270          */
30271         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
30272         /**
30273          * The keystroke filter mask to be applied on email input
30274          * @type RegExp
30275          */
30276         'emailMask' : /[a-z0-9_\.\-@]/i,
30277
30278         /**
30279          * The function used to validate URLs
30280          * @param {String} value The URL
30281          */
30282         'url' : function(v){
30283             return url.test(v);
30284         },
30285         /**
30286          * The error text to display when the url validation function returns false
30287          * @type String
30288          */
30289         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
30290         
30291         /**
30292          * The function used to validate alpha values
30293          * @param {String} value The value
30294          */
30295         'alpha' : function(v){
30296             return alpha.test(v);
30297         },
30298         /**
30299          * The error text to display when the alpha validation function returns false
30300          * @type String
30301          */
30302         'alphaText' : 'This field should only contain letters and _',
30303         /**
30304          * The keystroke filter mask to be applied on alpha input
30305          * @type RegExp
30306          */
30307         'alphaMask' : /[a-z_]/i,
30308
30309         /**
30310          * The function used to validate alphanumeric values
30311          * @param {String} value The value
30312          */
30313         'alphanum' : function(v){
30314             return alphanum.test(v);
30315         },
30316         /**
30317          * The error text to display when the alphanumeric validation function returns false
30318          * @type String
30319          */
30320         'alphanumText' : 'This field should only contain letters, numbers and _',
30321         /**
30322          * The keystroke filter mask to be applied on alphanumeric input
30323          * @type RegExp
30324          */
30325         'alphanumMask' : /[a-z0-9_]/i
30326     };
30327 }();//<script type="text/javascript">
30328
30329 /**
30330  * @class Roo.form.FCKeditor
30331  * @extends Roo.form.TextArea
30332  * Wrapper around the FCKEditor http://www.fckeditor.net
30333  * @constructor
30334  * Creates a new FCKeditor
30335  * @param {Object} config Configuration options
30336  */
30337 Roo.form.FCKeditor = function(config){
30338     Roo.form.FCKeditor.superclass.constructor.call(this, config);
30339     this.addEvents({
30340          /**
30341          * @event editorinit
30342          * Fired when the editor is initialized - you can add extra handlers here..
30343          * @param {FCKeditor} this
30344          * @param {Object} the FCK object.
30345          */
30346         editorinit : true
30347     });
30348     
30349     
30350 };
30351 Roo.form.FCKeditor.editors = { };
30352 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
30353 {
30354     //defaultAutoCreate : {
30355     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
30356     //},
30357     // private
30358     /**
30359      * @cfg {Object} fck options - see fck manual for details.
30360      */
30361     fckconfig : false,
30362     
30363     /**
30364      * @cfg {Object} fck toolbar set (Basic or Default)
30365      */
30366     toolbarSet : 'Basic',
30367     /**
30368      * @cfg {Object} fck BasePath
30369      */ 
30370     basePath : '/fckeditor/',
30371     
30372     
30373     frame : false,
30374     
30375     value : '',
30376     
30377    
30378     onRender : function(ct, position)
30379     {
30380         if(!this.el){
30381             this.defaultAutoCreate = {
30382                 tag: "textarea",
30383                 style:"width:300px;height:60px;",
30384                 autocomplete: "new-password"
30385             };
30386         }
30387         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
30388         /*
30389         if(this.grow){
30390             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
30391             if(this.preventScrollbars){
30392                 this.el.setStyle("overflow", "hidden");
30393             }
30394             this.el.setHeight(this.growMin);
30395         }
30396         */
30397         //console.log('onrender' + this.getId() );
30398         Roo.form.FCKeditor.editors[this.getId()] = this;
30399          
30400
30401         this.replaceTextarea() ;
30402         
30403     },
30404     
30405     getEditor : function() {
30406         return this.fckEditor;
30407     },
30408     /**
30409      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
30410      * @param {Mixed} value The value to set
30411      */
30412     
30413     
30414     setValue : function(value)
30415     {
30416         //console.log('setValue: ' + value);
30417         
30418         if(typeof(value) == 'undefined') { // not sure why this is happending...
30419             return;
30420         }
30421         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
30422         
30423         //if(!this.el || !this.getEditor()) {
30424         //    this.value = value;
30425             //this.setValue.defer(100,this,[value]);    
30426         //    return;
30427         //} 
30428         
30429         if(!this.getEditor()) {
30430             return;
30431         }
30432         
30433         this.getEditor().SetData(value);
30434         
30435         //
30436
30437     },
30438
30439     /**
30440      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
30441      * @return {Mixed} value The field value
30442      */
30443     getValue : function()
30444     {
30445         
30446         if (this.frame && this.frame.dom.style.display == 'none') {
30447             return Roo.form.FCKeditor.superclass.getValue.call(this);
30448         }
30449         
30450         if(!this.el || !this.getEditor()) {
30451            
30452            // this.getValue.defer(100,this); 
30453             return this.value;
30454         }
30455        
30456         
30457         var value=this.getEditor().GetData();
30458         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
30459         return Roo.form.FCKeditor.superclass.getValue.call(this);
30460         
30461
30462     },
30463
30464     /**
30465      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
30466      * @return {Mixed} value The field value
30467      */
30468     getRawValue : function()
30469     {
30470         if (this.frame && this.frame.dom.style.display == 'none') {
30471             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30472         }
30473         
30474         if(!this.el || !this.getEditor()) {
30475             //this.getRawValue.defer(100,this); 
30476             return this.value;
30477             return;
30478         }
30479         
30480         
30481         
30482         var value=this.getEditor().GetData();
30483         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
30484         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30485          
30486     },
30487     
30488     setSize : function(w,h) {
30489         
30490         
30491         
30492         //if (this.frame && this.frame.dom.style.display == 'none') {
30493         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30494         //    return;
30495         //}
30496         //if(!this.el || !this.getEditor()) {
30497         //    this.setSize.defer(100,this, [w,h]); 
30498         //    return;
30499         //}
30500         
30501         
30502         
30503         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30504         
30505         this.frame.dom.setAttribute('width', w);
30506         this.frame.dom.setAttribute('height', h);
30507         this.frame.setSize(w,h);
30508         
30509     },
30510     
30511     toggleSourceEdit : function(value) {
30512         
30513       
30514          
30515         this.el.dom.style.display = value ? '' : 'none';
30516         this.frame.dom.style.display = value ?  'none' : '';
30517         
30518     },
30519     
30520     
30521     focus: function(tag)
30522     {
30523         if (this.frame.dom.style.display == 'none') {
30524             return Roo.form.FCKeditor.superclass.focus.call(this);
30525         }
30526         if(!this.el || !this.getEditor()) {
30527             this.focus.defer(100,this, [tag]); 
30528             return;
30529         }
30530         
30531         
30532         
30533         
30534         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
30535         this.getEditor().Focus();
30536         if (tgs.length) {
30537             if (!this.getEditor().Selection.GetSelection()) {
30538                 this.focus.defer(100,this, [tag]); 
30539                 return;
30540             }
30541             
30542             
30543             var r = this.getEditor().EditorDocument.createRange();
30544             r.setStart(tgs[0],0);
30545             r.setEnd(tgs[0],0);
30546             this.getEditor().Selection.GetSelection().removeAllRanges();
30547             this.getEditor().Selection.GetSelection().addRange(r);
30548             this.getEditor().Focus();
30549         }
30550         
30551     },
30552     
30553     
30554     
30555     replaceTextarea : function()
30556     {
30557         if ( document.getElementById( this.getId() + '___Frame' ) )
30558             return ;
30559         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
30560         //{
30561             // We must check the elements firstly using the Id and then the name.
30562         var oTextarea = document.getElementById( this.getId() );
30563         
30564         var colElementsByName = document.getElementsByName( this.getId() ) ;
30565          
30566         oTextarea.style.display = 'none' ;
30567
30568         if ( oTextarea.tabIndex ) {            
30569             this.TabIndex = oTextarea.tabIndex ;
30570         }
30571         
30572         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
30573         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
30574         this.frame = Roo.get(this.getId() + '___Frame')
30575     },
30576     
30577     _getConfigHtml : function()
30578     {
30579         var sConfig = '' ;
30580
30581         for ( var o in this.fckconfig ) {
30582             sConfig += sConfig.length > 0  ? '&amp;' : '';
30583             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
30584         }
30585
30586         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
30587     },
30588     
30589     
30590     _getIFrameHtml : function()
30591     {
30592         var sFile = 'fckeditor.html' ;
30593         /* no idea what this is about..
30594         try
30595         {
30596             if ( (/fcksource=true/i).test( window.top.location.search ) )
30597                 sFile = 'fckeditor.original.html' ;
30598         }
30599         catch (e) { 
30600         */
30601
30602         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
30603         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
30604         
30605         
30606         var html = '<iframe id="' + this.getId() +
30607             '___Frame" src="' + sLink +
30608             '" width="' + this.width +
30609             '" height="' + this.height + '"' +
30610             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
30611             ' frameborder="0" scrolling="no"></iframe>' ;
30612
30613         return html ;
30614     },
30615     
30616     _insertHtmlBefore : function( html, element )
30617     {
30618         if ( element.insertAdjacentHTML )       {
30619             // IE
30620             element.insertAdjacentHTML( 'beforeBegin', html ) ;
30621         } else { // Gecko
30622             var oRange = document.createRange() ;
30623             oRange.setStartBefore( element ) ;
30624             var oFragment = oRange.createContextualFragment( html );
30625             element.parentNode.insertBefore( oFragment, element ) ;
30626         }
30627     }
30628     
30629     
30630   
30631     
30632     
30633     
30634     
30635
30636 });
30637
30638 //Roo.reg('fckeditor', Roo.form.FCKeditor);
30639
30640 function FCKeditor_OnComplete(editorInstance){
30641     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
30642     f.fckEditor = editorInstance;
30643     //console.log("loaded");
30644     f.fireEvent('editorinit', f, editorInstance);
30645
30646   
30647
30648  
30649
30650
30651
30652
30653
30654
30655
30656
30657
30658
30659
30660
30661
30662
30663
30664 //<script type="text/javascript">
30665 /**
30666  * @class Roo.form.GridField
30667  * @extends Roo.form.Field
30668  * Embed a grid (or editable grid into a form)
30669  * STATUS ALPHA
30670  * 
30671  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
30672  * it needs 
30673  * xgrid.store = Roo.data.Store
30674  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
30675  * xgrid.store.reader = Roo.data.JsonReader 
30676  * 
30677  * 
30678  * @constructor
30679  * Creates a new GridField
30680  * @param {Object} config Configuration options
30681  */
30682 Roo.form.GridField = function(config){
30683     Roo.form.GridField.superclass.constructor.call(this, config);
30684      
30685 };
30686
30687 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
30688     /**
30689      * @cfg {Number} width  - used to restrict width of grid..
30690      */
30691     width : 100,
30692     /**
30693      * @cfg {Number} height - used to restrict height of grid..
30694      */
30695     height : 50,
30696      /**
30697      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
30698          * 
30699          *}
30700      */
30701     xgrid : false, 
30702     /**
30703      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30704      * {tag: "input", type: "checkbox", autocomplete: "off"})
30705      */
30706    // defaultAutoCreate : { tag: 'div' },
30707     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
30708     /**
30709      * @cfg {String} addTitle Text to include for adding a title.
30710      */
30711     addTitle : false,
30712     //
30713     onResize : function(){
30714         Roo.form.Field.superclass.onResize.apply(this, arguments);
30715     },
30716
30717     initEvents : function(){
30718         // Roo.form.Checkbox.superclass.initEvents.call(this);
30719         // has no events...
30720        
30721     },
30722
30723
30724     getResizeEl : function(){
30725         return this.wrap;
30726     },
30727
30728     getPositionEl : function(){
30729         return this.wrap;
30730     },
30731
30732     // private
30733     onRender : function(ct, position){
30734         
30735         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
30736         var style = this.style;
30737         delete this.style;
30738         
30739         Roo.form.GridField.superclass.onRender.call(this, ct, position);
30740         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
30741         this.viewEl = this.wrap.createChild({ tag: 'div' });
30742         if (style) {
30743             this.viewEl.applyStyles(style);
30744         }
30745         if (this.width) {
30746             this.viewEl.setWidth(this.width);
30747         }
30748         if (this.height) {
30749             this.viewEl.setHeight(this.height);
30750         }
30751         //if(this.inputValue !== undefined){
30752         //this.setValue(this.value);
30753         
30754         
30755         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
30756         
30757         
30758         this.grid.render();
30759         this.grid.getDataSource().on('remove', this.refreshValue, this);
30760         this.grid.getDataSource().on('update', this.refreshValue, this);
30761         this.grid.on('afteredit', this.refreshValue, this);
30762  
30763     },
30764      
30765     
30766     /**
30767      * Sets the value of the item. 
30768      * @param {String} either an object  or a string..
30769      */
30770     setValue : function(v){
30771         //this.value = v;
30772         v = v || []; // empty set..
30773         // this does not seem smart - it really only affects memoryproxy grids..
30774         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
30775             var ds = this.grid.getDataSource();
30776             // assumes a json reader..
30777             var data = {}
30778             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
30779             ds.loadData( data);
30780         }
30781         // clear selection so it does not get stale.
30782         if (this.grid.sm) { 
30783             this.grid.sm.clearSelections();
30784         }
30785         
30786         Roo.form.GridField.superclass.setValue.call(this, v);
30787         this.refreshValue();
30788         // should load data in the grid really....
30789     },
30790     
30791     // private
30792     refreshValue: function() {
30793          var val = [];
30794         this.grid.getDataSource().each(function(r) {
30795             val.push(r.data);
30796         });
30797         this.el.dom.value = Roo.encode(val);
30798     }
30799     
30800      
30801     
30802     
30803 });/*
30804  * Based on:
30805  * Ext JS Library 1.1.1
30806  * Copyright(c) 2006-2007, Ext JS, LLC.
30807  *
30808  * Originally Released Under LGPL - original licence link has changed is not relivant.
30809  *
30810  * Fork - LGPL
30811  * <script type="text/javascript">
30812  */
30813 /**
30814  * @class Roo.form.DisplayField
30815  * @extends Roo.form.Field
30816  * A generic Field to display non-editable data.
30817  * @constructor
30818  * Creates a new Display Field item.
30819  * @param {Object} config Configuration options
30820  */
30821 Roo.form.DisplayField = function(config){
30822     Roo.form.DisplayField.superclass.constructor.call(this, config);
30823     
30824 };
30825
30826 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
30827     inputType:      'hidden',
30828     allowBlank:     true,
30829     readOnly:         true,
30830     
30831  
30832     /**
30833      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30834      */
30835     focusClass : undefined,
30836     /**
30837      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30838      */
30839     fieldClass: 'x-form-field',
30840     
30841      /**
30842      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
30843      */
30844     valueRenderer: undefined,
30845     
30846     width: 100,
30847     /**
30848      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30849      * {tag: "input", type: "checkbox", autocomplete: "off"})
30850      */
30851      
30852  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30853
30854     onResize : function(){
30855         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
30856         
30857     },
30858
30859     initEvents : function(){
30860         // Roo.form.Checkbox.superclass.initEvents.call(this);
30861         // has no events...
30862        
30863     },
30864
30865
30866     getResizeEl : function(){
30867         return this.wrap;
30868     },
30869
30870     getPositionEl : function(){
30871         return this.wrap;
30872     },
30873
30874     // private
30875     onRender : function(ct, position){
30876         
30877         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
30878         //if(this.inputValue !== undefined){
30879         this.wrap = this.el.wrap();
30880         
30881         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
30882         
30883         if (this.bodyStyle) {
30884             this.viewEl.applyStyles(this.bodyStyle);
30885         }
30886         //this.viewEl.setStyle('padding', '2px');
30887         
30888         this.setValue(this.value);
30889         
30890     },
30891 /*
30892     // private
30893     initValue : Roo.emptyFn,
30894
30895   */
30896
30897         // private
30898     onClick : function(){
30899         
30900     },
30901
30902     /**
30903      * Sets the checked state of the checkbox.
30904      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
30905      */
30906     setValue : function(v){
30907         this.value = v;
30908         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
30909         // this might be called before we have a dom element..
30910         if (!this.viewEl) {
30911             return;
30912         }
30913         this.viewEl.dom.innerHTML = html;
30914         Roo.form.DisplayField.superclass.setValue.call(this, v);
30915
30916     }
30917 });/*
30918  * 
30919  * Licence- LGPL
30920  * 
30921  */
30922
30923 /**
30924  * @class Roo.form.DayPicker
30925  * @extends Roo.form.Field
30926  * A Day picker show [M] [T] [W] ....
30927  * @constructor
30928  * Creates a new Day Picker
30929  * @param {Object} config Configuration options
30930  */
30931 Roo.form.DayPicker= function(config){
30932     Roo.form.DayPicker.superclass.constructor.call(this, config);
30933      
30934 };
30935
30936 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
30937     /**
30938      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30939      */
30940     focusClass : undefined,
30941     /**
30942      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30943      */
30944     fieldClass: "x-form-field",
30945    
30946     /**
30947      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30948      * {tag: "input", type: "checkbox", autocomplete: "off"})
30949      */
30950     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
30951     
30952    
30953     actionMode : 'viewEl', 
30954     //
30955     // private
30956  
30957     inputType : 'hidden',
30958     
30959      
30960     inputElement: false, // real input element?
30961     basedOn: false, // ????
30962     
30963     isFormField: true, // not sure where this is needed!!!!
30964
30965     onResize : function(){
30966         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
30967         if(!this.boxLabel){
30968             this.el.alignTo(this.wrap, 'c-c');
30969         }
30970     },
30971
30972     initEvents : function(){
30973         Roo.form.Checkbox.superclass.initEvents.call(this);
30974         this.el.on("click", this.onClick,  this);
30975         this.el.on("change", this.onClick,  this);
30976     },
30977
30978
30979     getResizeEl : function(){
30980         return this.wrap;
30981     },
30982
30983     getPositionEl : function(){
30984         return this.wrap;
30985     },
30986
30987     
30988     // private
30989     onRender : function(ct, position){
30990         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
30991        
30992         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
30993         
30994         var r1 = '<table><tr>';
30995         var r2 = '<tr class="x-form-daypick-icons">';
30996         for (var i=0; i < 7; i++) {
30997             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
30998             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
30999         }
31000         
31001         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
31002         viewEl.select('img').on('click', this.onClick, this);
31003         this.viewEl = viewEl;   
31004         
31005         
31006         // this will not work on Chrome!!!
31007         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
31008         this.el.on('propertychange', this.setFromHidden,  this);  //ie
31009         
31010         
31011           
31012
31013     },
31014
31015     // private
31016     initValue : Roo.emptyFn,
31017
31018     /**
31019      * Returns the checked state of the checkbox.
31020      * @return {Boolean} True if checked, else false
31021      */
31022     getValue : function(){
31023         return this.el.dom.value;
31024         
31025     },
31026
31027         // private
31028     onClick : function(e){ 
31029         //this.setChecked(!this.checked);
31030         Roo.get(e.target).toggleClass('x-menu-item-checked');
31031         this.refreshValue();
31032         //if(this.el.dom.checked != this.checked){
31033         //    this.setValue(this.el.dom.checked);
31034        // }
31035     },
31036     
31037     // private
31038     refreshValue : function()
31039     {
31040         var val = '';
31041         this.viewEl.select('img',true).each(function(e,i,n)  {
31042             val += e.is(".x-menu-item-checked") ? String(n) : '';
31043         });
31044         this.setValue(val, true);
31045     },
31046
31047     /**
31048      * Sets the checked state of the checkbox.
31049      * On is always based on a string comparison between inputValue and the param.
31050      * @param {Boolean/String} value - the value to set 
31051      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
31052      */
31053     setValue : function(v,suppressEvent){
31054         if (!this.el.dom) {
31055             return;
31056         }
31057         var old = this.el.dom.value ;
31058         this.el.dom.value = v;
31059         if (suppressEvent) {
31060             return ;
31061         }
31062          
31063         // update display..
31064         this.viewEl.select('img',true).each(function(e,i,n)  {
31065             
31066             var on = e.is(".x-menu-item-checked");
31067             var newv = v.indexOf(String(n)) > -1;
31068             if (on != newv) {
31069                 e.toggleClass('x-menu-item-checked');
31070             }
31071             
31072         });
31073         
31074         
31075         this.fireEvent('change', this, v, old);
31076         
31077         
31078     },
31079    
31080     // handle setting of hidden value by some other method!!?!?
31081     setFromHidden: function()
31082     {
31083         if(!this.el){
31084             return;
31085         }
31086         //console.log("SET FROM HIDDEN");
31087         //alert('setFrom hidden');
31088         this.setValue(this.el.dom.value);
31089     },
31090     
31091     onDestroy : function()
31092     {
31093         if(this.viewEl){
31094             Roo.get(this.viewEl).remove();
31095         }
31096          
31097         Roo.form.DayPicker.superclass.onDestroy.call(this);
31098     }
31099
31100 });/*
31101  * RooJS Library 1.1.1
31102  * Copyright(c) 2008-2011  Alan Knowles
31103  *
31104  * License - LGPL
31105  */
31106  
31107
31108 /**
31109  * @class Roo.form.ComboCheck
31110  * @extends Roo.form.ComboBox
31111  * A combobox for multiple select items.
31112  *
31113  * FIXME - could do with a reset button..
31114  * 
31115  * @constructor
31116  * Create a new ComboCheck
31117  * @param {Object} config Configuration options
31118  */
31119 Roo.form.ComboCheck = function(config){
31120     Roo.form.ComboCheck.superclass.constructor.call(this, config);
31121     // should verify some data...
31122     // like
31123     // hiddenName = required..
31124     // displayField = required
31125     // valudField == required
31126     var req= [ 'hiddenName', 'displayField', 'valueField' ];
31127     var _t = this;
31128     Roo.each(req, function(e) {
31129         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
31130             throw "Roo.form.ComboCheck : missing value for: " + e;
31131         }
31132     });
31133     
31134     
31135 };
31136
31137 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
31138      
31139      
31140     editable : false,
31141      
31142     selectedClass: 'x-menu-item-checked', 
31143     
31144     // private
31145     onRender : function(ct, position){
31146         var _t = this;
31147         
31148         
31149         
31150         if(!this.tpl){
31151             var cls = 'x-combo-list';
31152
31153             
31154             this.tpl =  new Roo.Template({
31155                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
31156                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
31157                    '<span>{' + this.displayField + '}</span>' +
31158                     '</div>' 
31159                 
31160             });
31161         }
31162  
31163         
31164         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
31165         this.view.singleSelect = false;
31166         this.view.multiSelect = true;
31167         this.view.toggleSelect = true;
31168         this.pageTb.add(new Roo.Toolbar.Fill(), {
31169             
31170             text: 'Done',
31171             handler: function()
31172             {
31173                 _t.collapse();
31174             }
31175         });
31176     },
31177     
31178     onViewOver : function(e, t){
31179         // do nothing...
31180         return;
31181         
31182     },
31183     
31184     onViewClick : function(doFocus,index){
31185         return;
31186         
31187     },
31188     select: function () {
31189         //Roo.log("SELECT CALLED");
31190     },
31191      
31192     selectByValue : function(xv, scrollIntoView){
31193         var ar = this.getValueArray();
31194         var sels = [];
31195         
31196         Roo.each(ar, function(v) {
31197             if(v === undefined || v === null){
31198                 return;
31199             }
31200             var r = this.findRecord(this.valueField, v);
31201             if(r){
31202                 sels.push(this.store.indexOf(r))
31203                 
31204             }
31205         },this);
31206         this.view.select(sels);
31207         return false;
31208     },
31209     
31210     
31211     
31212     onSelect : function(record, index){
31213        // Roo.log("onselect Called");
31214        // this is only called by the clear button now..
31215         this.view.clearSelections();
31216         this.setValue('[]');
31217         if (this.value != this.valueBefore) {
31218             this.fireEvent('change', this, this.value, this.valueBefore);
31219             this.valueBefore = this.value;
31220         }
31221     },
31222     getValueArray : function()
31223     {
31224         var ar = [] ;
31225         
31226         try {
31227             //Roo.log(this.value);
31228             if (typeof(this.value) == 'undefined') {
31229                 return [];
31230             }
31231             var ar = Roo.decode(this.value);
31232             return  ar instanceof Array ? ar : []; //?? valid?
31233             
31234         } catch(e) {
31235             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
31236             return [];
31237         }
31238          
31239     },
31240     expand : function ()
31241     {
31242         
31243         Roo.form.ComboCheck.superclass.expand.call(this);
31244         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
31245         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
31246         
31247
31248     },
31249     
31250     collapse : function(){
31251         Roo.form.ComboCheck.superclass.collapse.call(this);
31252         var sl = this.view.getSelectedIndexes();
31253         var st = this.store;
31254         var nv = [];
31255         var tv = [];
31256         var r;
31257         Roo.each(sl, function(i) {
31258             r = st.getAt(i);
31259             nv.push(r.get(this.valueField));
31260         },this);
31261         this.setValue(Roo.encode(nv));
31262         if (this.value != this.valueBefore) {
31263
31264             this.fireEvent('change', this, this.value, this.valueBefore);
31265             this.valueBefore = this.value;
31266         }
31267         
31268     },
31269     
31270     setValue : function(v){
31271         // Roo.log(v);
31272         this.value = v;
31273         
31274         var vals = this.getValueArray();
31275         var tv = [];
31276         Roo.each(vals, function(k) {
31277             var r = this.findRecord(this.valueField, k);
31278             if(r){
31279                 tv.push(r.data[this.displayField]);
31280             }else if(this.valueNotFoundText !== undefined){
31281                 tv.push( this.valueNotFoundText );
31282             }
31283         },this);
31284        // Roo.log(tv);
31285         
31286         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
31287         this.hiddenField.value = v;
31288         this.value = v;
31289     }
31290     
31291 });/*
31292  * Based on:
31293  * Ext JS Library 1.1.1
31294  * Copyright(c) 2006-2007, Ext JS, LLC.
31295  *
31296  * Originally Released Under LGPL - original licence link has changed is not relivant.
31297  *
31298  * Fork - LGPL
31299  * <script type="text/javascript">
31300  */
31301  
31302 /**
31303  * @class Roo.form.Signature
31304  * @extends Roo.form.Field
31305  * Signature field.  
31306  * @constructor
31307  * 
31308  * @param {Object} config Configuration options
31309  */
31310
31311 Roo.form.Signature = function(config){
31312     Roo.form.Signature.superclass.constructor.call(this, config);
31313     
31314     this.addEvents({// not in used??
31315          /**
31316          * @event confirm
31317          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
31318              * @param {Roo.form.Signature} combo This combo box
31319              */
31320         'confirm' : true,
31321         /**
31322          * @event reset
31323          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
31324              * @param {Roo.form.ComboBox} combo This combo box
31325              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
31326              */
31327         'reset' : true
31328     });
31329 };
31330
31331 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
31332     /**
31333      * @cfg {Object} labels Label to use when rendering a form.
31334      * defaults to 
31335      * labels : { 
31336      *      clear : "Clear",
31337      *      confirm : "Confirm"
31338      *  }
31339      */
31340     labels : { 
31341         clear : "Clear",
31342         confirm : "Confirm"
31343     },
31344     /**
31345      * @cfg {Number} width The signature panel width (defaults to 300)
31346      */
31347     width: 300,
31348     /**
31349      * @cfg {Number} height The signature panel height (defaults to 100)
31350      */
31351     height : 100,
31352     /**
31353      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
31354      */
31355     allowBlank : false,
31356     
31357     //private
31358     // {Object} signPanel The signature SVG panel element (defaults to {})
31359     signPanel : {},
31360     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
31361     isMouseDown : false,
31362     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
31363     isConfirmed : false,
31364     // {String} signatureTmp SVG mapping string (defaults to empty string)
31365     signatureTmp : '',
31366     
31367     
31368     defaultAutoCreate : { // modified by initCompnoent..
31369         tag: "input",
31370         type:"hidden"
31371     },
31372
31373     // private
31374     onRender : function(ct, position){
31375         
31376         Roo.form.Signature.superclass.onRender.call(this, ct, position);
31377         
31378         this.wrap = this.el.wrap({
31379             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
31380         });
31381         
31382         this.createToolbar(this);
31383         this.signPanel = this.wrap.createChild({
31384                 tag: 'div',
31385                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
31386             }, this.el
31387         );
31388             
31389         this.svgID = Roo.id();
31390         this.svgEl = this.signPanel.createChild({
31391               xmlns : 'http://www.w3.org/2000/svg',
31392               tag : 'svg',
31393               id : this.svgID + "-svg",
31394               width: this.width,
31395               height: this.height,
31396               viewBox: '0 0 '+this.width+' '+this.height,
31397               cn : [
31398                 {
31399                     tag: "rect",
31400                     id: this.svgID + "-svg-r",
31401                     width: this.width,
31402                     height: this.height,
31403                     fill: "#ffa"
31404                 },
31405                 {
31406                     tag: "line",
31407                     id: this.svgID + "-svg-l",
31408                     x1: "0", // start
31409                     y1: (this.height*0.8), // start set the line in 80% of height
31410                     x2: this.width, // end
31411                     y2: (this.height*0.8), // end set the line in 80% of height
31412                     'stroke': "#666",
31413                     'stroke-width': "1",
31414                     'stroke-dasharray': "3",
31415                     'shape-rendering': "crispEdges",
31416                     'pointer-events': "none"
31417                 },
31418                 {
31419                     tag: "path",
31420                     id: this.svgID + "-svg-p",
31421                     'stroke': "navy",
31422                     'stroke-width': "3",
31423                     'fill': "none",
31424                     'pointer-events': 'none'
31425                 }
31426               ]
31427         });
31428         this.createSVG();
31429         this.svgBox = this.svgEl.dom.getScreenCTM();
31430     },
31431     createSVG : function(){ 
31432         var svg = this.signPanel;
31433         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
31434         var t = this;
31435
31436         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
31437         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
31438         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
31439         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
31440         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
31441         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
31442         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
31443         
31444     },
31445     isTouchEvent : function(e){
31446         return e.type.match(/^touch/);
31447     },
31448     getCoords : function (e) {
31449         var pt    = this.svgEl.dom.createSVGPoint();
31450         pt.x = e.clientX; 
31451         pt.y = e.clientY;
31452         if (this.isTouchEvent(e)) {
31453             pt.x =  e.targetTouches[0].clientX;
31454             pt.y = e.targetTouches[0].clientY;
31455         }
31456         var a = this.svgEl.dom.getScreenCTM();
31457         var b = a.inverse();
31458         var mx = pt.matrixTransform(b);
31459         return mx.x + ',' + mx.y;
31460     },
31461     //mouse event headler 
31462     down : function (e) {
31463         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
31464         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
31465         
31466         this.isMouseDown = true;
31467         
31468         e.preventDefault();
31469     },
31470     move : function (e) {
31471         if (this.isMouseDown) {
31472             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
31473             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
31474         }
31475         
31476         e.preventDefault();
31477     },
31478     up : function (e) {
31479         this.isMouseDown = false;
31480         var sp = this.signatureTmp.split(' ');
31481         
31482         if(sp.length > 1){
31483             if(!sp[sp.length-2].match(/^L/)){
31484                 sp.pop();
31485                 sp.pop();
31486                 sp.push("");
31487                 this.signatureTmp = sp.join(" ");
31488             }
31489         }
31490         if(this.getValue() != this.signatureTmp){
31491             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31492             this.isConfirmed = false;
31493         }
31494         e.preventDefault();
31495     },
31496     
31497     /**
31498      * Protected method that will not generally be called directly. It
31499      * is called when the editor creates its toolbar. Override this method if you need to
31500      * add custom toolbar buttons.
31501      * @param {HtmlEditor} editor
31502      */
31503     createToolbar : function(editor){
31504          function btn(id, toggle, handler){
31505             var xid = fid + '-'+ id ;
31506             return {
31507                 id : xid,
31508                 cmd : id,
31509                 cls : 'x-btn-icon x-edit-'+id,
31510                 enableToggle:toggle !== false,
31511                 scope: editor, // was editor...
31512                 handler:handler||editor.relayBtnCmd,
31513                 clickEvent:'mousedown',
31514                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
31515                 tabIndex:-1
31516             };
31517         }
31518         
31519         
31520         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
31521         this.tb = tb;
31522         this.tb.add(
31523            {
31524                 cls : ' x-signature-btn x-signature-'+id,
31525                 scope: editor, // was editor...
31526                 handler: this.reset,
31527                 clickEvent:'mousedown',
31528                 text: this.labels.clear
31529             },
31530             {
31531                  xtype : 'Fill',
31532                  xns: Roo.Toolbar
31533             }, 
31534             {
31535                 cls : '  x-signature-btn x-signature-'+id,
31536                 scope: editor, // was editor...
31537                 handler: this.confirmHandler,
31538                 clickEvent:'mousedown',
31539                 text: this.labels.confirm
31540             }
31541         );
31542     
31543     },
31544     //public
31545     /**
31546      * when user is clicked confirm then show this image.....
31547      * 
31548      * @return {String} Image Data URI
31549      */
31550     getImageDataURI : function(){
31551         var svg = this.svgEl.dom.parentNode.innerHTML;
31552         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
31553         return src; 
31554     },
31555     /**
31556      * 
31557      * @return {Boolean} this.isConfirmed
31558      */
31559     getConfirmed : function(){
31560         return this.isConfirmed;
31561     },
31562     /**
31563      * 
31564      * @return {Number} this.width
31565      */
31566     getWidth : function(){
31567         return this.width;
31568     },
31569     /**
31570      * 
31571      * @return {Number} this.height
31572      */
31573     getHeight : function(){
31574         return this.height;
31575     },
31576     // private
31577     getSignature : function(){
31578         return this.signatureTmp;
31579     },
31580     // private
31581     reset : function(){
31582         this.signatureTmp = '';
31583         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31584         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
31585         this.isConfirmed = false;
31586         Roo.form.Signature.superclass.reset.call(this);
31587     },
31588     setSignature : function(s){
31589         this.signatureTmp = s;
31590         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31591         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
31592         this.setValue(s);
31593         this.isConfirmed = false;
31594         Roo.form.Signature.superclass.reset.call(this);
31595     }, 
31596     test : function(){
31597 //        Roo.log(this.signPanel.dom.contentWindow.up())
31598     },
31599     //private
31600     setConfirmed : function(){
31601         
31602         
31603         
31604 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
31605     },
31606     // private
31607     confirmHandler : function(){
31608         if(!this.getSignature()){
31609             return;
31610         }
31611         
31612         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
31613         this.setValue(this.getSignature());
31614         this.isConfirmed = true;
31615         
31616         this.fireEvent('confirm', this);
31617     },
31618     // private
31619     // Subclasses should provide the validation implementation by overriding this
31620     validateValue : function(value){
31621         if(this.allowBlank){
31622             return true;
31623         }
31624         
31625         if(this.isConfirmed){
31626             return true;
31627         }
31628         return false;
31629     }
31630 });/*
31631  * Based on:
31632  * Ext JS Library 1.1.1
31633  * Copyright(c) 2006-2007, Ext JS, LLC.
31634  *
31635  * Originally Released Under LGPL - original licence link has changed is not relivant.
31636  *
31637  * Fork - LGPL
31638  * <script type="text/javascript">
31639  */
31640  
31641
31642 /**
31643  * @class Roo.form.ComboBox
31644  * @extends Roo.form.TriggerField
31645  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
31646  * @constructor
31647  * Create a new ComboBox.
31648  * @param {Object} config Configuration options
31649  */
31650 Roo.form.Select = function(config){
31651     Roo.form.Select.superclass.constructor.call(this, config);
31652      
31653 };
31654
31655 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
31656     /**
31657      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
31658      */
31659     /**
31660      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
31661      * rendering into an Roo.Editor, defaults to false)
31662      */
31663     /**
31664      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
31665      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
31666      */
31667     /**
31668      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
31669      */
31670     /**
31671      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
31672      * the dropdown list (defaults to undefined, with no header element)
31673      */
31674
31675      /**
31676      * @cfg {String/Roo.Template} tpl The template to use to render the output
31677      */
31678      
31679     // private
31680     defaultAutoCreate : {tag: "select"  },
31681     /**
31682      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
31683      */
31684     listWidth: undefined,
31685     /**
31686      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
31687      * mode = 'remote' or 'text' if mode = 'local')
31688      */
31689     displayField: undefined,
31690     /**
31691      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
31692      * mode = 'remote' or 'value' if mode = 'local'). 
31693      * Note: use of a valueField requires the user make a selection
31694      * in order for a value to be mapped.
31695      */
31696     valueField: undefined,
31697     
31698     
31699     /**
31700      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
31701      * field's data value (defaults to the underlying DOM element's name)
31702      */
31703     hiddenName: undefined,
31704     /**
31705      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
31706      */
31707     listClass: '',
31708     /**
31709      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
31710      */
31711     selectedClass: 'x-combo-selected',
31712     /**
31713      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
31714      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
31715      * which displays a downward arrow icon).
31716      */
31717     triggerClass : 'x-form-arrow-trigger',
31718     /**
31719      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31720      */
31721     shadow:'sides',
31722     /**
31723      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
31724      * anchor positions (defaults to 'tl-bl')
31725      */
31726     listAlign: 'tl-bl?',
31727     /**
31728      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
31729      */
31730     maxHeight: 300,
31731     /**
31732      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
31733      * query specified by the allQuery config option (defaults to 'query')
31734      */
31735     triggerAction: 'query',
31736     /**
31737      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
31738      * (defaults to 4, does not apply if editable = false)
31739      */
31740     minChars : 4,
31741     /**
31742      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
31743      * delay (typeAheadDelay) if it matches a known value (defaults to false)
31744      */
31745     typeAhead: false,
31746     /**
31747      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
31748      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
31749      */
31750     queryDelay: 500,
31751     /**
31752      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
31753      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
31754      */
31755     pageSize: 0,
31756     /**
31757      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
31758      * when editable = true (defaults to false)
31759      */
31760     selectOnFocus:false,
31761     /**
31762      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
31763      */
31764     queryParam: 'query',
31765     /**
31766      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
31767      * when mode = 'remote' (defaults to 'Loading...')
31768      */
31769     loadingText: 'Loading...',
31770     /**
31771      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
31772      */
31773     resizable: false,
31774     /**
31775      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
31776      */
31777     handleHeight : 8,
31778     /**
31779      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
31780      * traditional select (defaults to true)
31781      */
31782     editable: true,
31783     /**
31784      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
31785      */
31786     allQuery: '',
31787     /**
31788      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
31789      */
31790     mode: 'remote',
31791     /**
31792      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
31793      * listWidth has a higher value)
31794      */
31795     minListWidth : 70,
31796     /**
31797      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
31798      * allow the user to set arbitrary text into the field (defaults to false)
31799      */
31800     forceSelection:false,
31801     /**
31802      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
31803      * if typeAhead = true (defaults to 250)
31804      */
31805     typeAheadDelay : 250,
31806     /**
31807      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
31808      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
31809      */
31810     valueNotFoundText : undefined,
31811     
31812     /**
31813      * @cfg {String} defaultValue The value displayed after loading the store.
31814      */
31815     defaultValue: '',
31816     
31817     /**
31818      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
31819      */
31820     blockFocus : false,
31821     
31822     /**
31823      * @cfg {Boolean} disableClear Disable showing of clear button.
31824      */
31825     disableClear : false,
31826     /**
31827      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
31828      */
31829     alwaysQuery : false,
31830     
31831     //private
31832     addicon : false,
31833     editicon: false,
31834     
31835     // element that contains real text value.. (when hidden is used..)
31836      
31837     // private
31838     onRender : function(ct, position){
31839         Roo.form.Field.prototype.onRender.call(this, ct, position);
31840         
31841         if(this.store){
31842             this.store.on('beforeload', this.onBeforeLoad, this);
31843             this.store.on('load', this.onLoad, this);
31844             this.store.on('loadexception', this.onLoadException, this);
31845             this.store.load({});
31846         }
31847         
31848         
31849         
31850     },
31851
31852     // private
31853     initEvents : function(){
31854         //Roo.form.ComboBox.superclass.initEvents.call(this);
31855  
31856     },
31857
31858     onDestroy : function(){
31859        
31860         if(this.store){
31861             this.store.un('beforeload', this.onBeforeLoad, this);
31862             this.store.un('load', this.onLoad, this);
31863             this.store.un('loadexception', this.onLoadException, this);
31864         }
31865         //Roo.form.ComboBox.superclass.onDestroy.call(this);
31866     },
31867
31868     // private
31869     fireKey : function(e){
31870         if(e.isNavKeyPress() && !this.list.isVisible()){
31871             this.fireEvent("specialkey", this, e);
31872         }
31873     },
31874
31875     // private
31876     onResize: function(w, h){
31877         
31878         return; 
31879     
31880         
31881     },
31882
31883     /**
31884      * Allow or prevent the user from directly editing the field text.  If false is passed,
31885      * the user will only be able to select from the items defined in the dropdown list.  This method
31886      * is the runtime equivalent of setting the 'editable' config option at config time.
31887      * @param {Boolean} value True to allow the user to directly edit the field text
31888      */
31889     setEditable : function(value){
31890          
31891     },
31892
31893     // private
31894     onBeforeLoad : function(){
31895         
31896         Roo.log("Select before load");
31897         return;
31898     
31899         this.innerList.update(this.loadingText ?
31900                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
31901         //this.restrictHeight();
31902         this.selectedIndex = -1;
31903     },
31904
31905     // private
31906     onLoad : function(){
31907
31908     
31909         var dom = this.el.dom;
31910         dom.innerHTML = '';
31911          var od = dom.ownerDocument;
31912          
31913         if (this.emptyText) {
31914             var op = od.createElement('option');
31915             op.setAttribute('value', '');
31916             op.innerHTML = String.format('{0}', this.emptyText);
31917             dom.appendChild(op);
31918         }
31919         if(this.store.getCount() > 0){
31920            
31921             var vf = this.valueField;
31922             var df = this.displayField;
31923             this.store.data.each(function(r) {
31924                 // which colmsn to use... testing - cdoe / title..
31925                 var op = od.createElement('option');
31926                 op.setAttribute('value', r.data[vf]);
31927                 op.innerHTML = String.format('{0}', r.data[df]);
31928                 dom.appendChild(op);
31929             });
31930             if (typeof(this.defaultValue != 'undefined')) {
31931                 this.setValue(this.defaultValue);
31932             }
31933             
31934              
31935         }else{
31936             //this.onEmptyResults();
31937         }
31938         //this.el.focus();
31939     },
31940     // private
31941     onLoadException : function()
31942     {
31943         dom.innerHTML = '';
31944             
31945         Roo.log("Select on load exception");
31946         return;
31947     
31948         this.collapse();
31949         Roo.log(this.store.reader.jsonData);
31950         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
31951             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
31952         }
31953         
31954         
31955     },
31956     // private
31957     onTypeAhead : function(){
31958          
31959     },
31960
31961     // private
31962     onSelect : function(record, index){
31963         Roo.log('on select?');
31964         return;
31965         if(this.fireEvent('beforeselect', this, record, index) !== false){
31966             this.setFromData(index > -1 ? record.data : false);
31967             this.collapse();
31968             this.fireEvent('select', this, record, index);
31969         }
31970     },
31971
31972     /**
31973      * Returns the currently selected field value or empty string if no value is set.
31974      * @return {String} value The selected value
31975      */
31976     getValue : function(){
31977         var dom = this.el.dom;
31978         this.value = dom.options[dom.selectedIndex].value;
31979         return this.value;
31980         
31981     },
31982
31983     /**
31984      * Clears any text/value currently set in the field
31985      */
31986     clearValue : function(){
31987         this.value = '';
31988         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
31989         
31990     },
31991
31992     /**
31993      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
31994      * will be displayed in the field.  If the value does not match the data value of an existing item,
31995      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
31996      * Otherwise the field will be blank (although the value will still be set).
31997      * @param {String} value The value to match
31998      */
31999     setValue : function(v){
32000         var d = this.el.dom;
32001         for (var i =0; i < d.options.length;i++) {
32002             if (v == d.options[i].value) {
32003                 d.selectedIndex = i;
32004                 this.value = v;
32005                 return;
32006             }
32007         }
32008         this.clearValue();
32009     },
32010     /**
32011      * @property {Object} the last set data for the element
32012      */
32013     
32014     lastData : false,
32015     /**
32016      * Sets the value of the field based on a object which is related to the record format for the store.
32017      * @param {Object} value the value to set as. or false on reset?
32018      */
32019     setFromData : function(o){
32020         Roo.log('setfrom data?');
32021          
32022         
32023         
32024     },
32025     // private
32026     reset : function(){
32027         this.clearValue();
32028     },
32029     // private
32030     findRecord : function(prop, value){
32031         
32032         return false;
32033     
32034         var record;
32035         if(this.store.getCount() > 0){
32036             this.store.each(function(r){
32037                 if(r.data[prop] == value){
32038                     record = r;
32039                     return false;
32040                 }
32041                 return true;
32042             });
32043         }
32044         return record;
32045     },
32046     
32047     getName: function()
32048     {
32049         // returns hidden if it's set..
32050         if (!this.rendered) {return ''};
32051         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
32052         
32053     },
32054      
32055
32056     
32057
32058     // private
32059     onEmptyResults : function(){
32060         Roo.log('empty results');
32061         //this.collapse();
32062     },
32063
32064     /**
32065      * Returns true if the dropdown list is expanded, else false.
32066      */
32067     isExpanded : function(){
32068         return false;
32069     },
32070
32071     /**
32072      * Select an item in the dropdown list by its data value. 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 {String} value The data value of the 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      * @return {Boolean} True if the value matched an item in the list, else false
32078      */
32079     selectByValue : function(v, scrollIntoView){
32080         Roo.log('select By Value');
32081         return false;
32082     
32083         if(v !== undefined && v !== null){
32084             var r = this.findRecord(this.valueField || this.displayField, v);
32085             if(r){
32086                 this.select(this.store.indexOf(r), scrollIntoView);
32087                 return true;
32088             }
32089         }
32090         return false;
32091     },
32092
32093     /**
32094      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
32095      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32096      * @param {Number} index The zero-based index of the list item to select
32097      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32098      * selected item if it is not currently in view (defaults to true)
32099      */
32100     select : function(index, scrollIntoView){
32101         Roo.log('select ');
32102         return  ;
32103         
32104         this.selectedIndex = index;
32105         this.view.select(index);
32106         if(scrollIntoView !== false){
32107             var el = this.view.getNode(index);
32108             if(el){
32109                 this.innerList.scrollChildIntoView(el, false);
32110             }
32111         }
32112     },
32113
32114       
32115
32116     // private
32117     validateBlur : function(){
32118         
32119         return;
32120         
32121     },
32122
32123     // private
32124     initQuery : function(){
32125         this.doQuery(this.getRawValue());
32126     },
32127
32128     // private
32129     doForce : function(){
32130         if(this.el.dom.value.length > 0){
32131             this.el.dom.value =
32132                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
32133              
32134         }
32135     },
32136
32137     /**
32138      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
32139      * query allowing the query action to be canceled if needed.
32140      * @param {String} query The SQL query to execute
32141      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
32142      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
32143      * saved in the current store (defaults to false)
32144      */
32145     doQuery : function(q, forceAll){
32146         
32147         Roo.log('doQuery?');
32148         if(q === undefined || q === null){
32149             q = '';
32150         }
32151         var qe = {
32152             query: q,
32153             forceAll: forceAll,
32154             combo: this,
32155             cancel:false
32156         };
32157         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
32158             return false;
32159         }
32160         q = qe.query;
32161         forceAll = qe.forceAll;
32162         if(forceAll === true || (q.length >= this.minChars)){
32163             if(this.lastQuery != q || this.alwaysQuery){
32164                 this.lastQuery = q;
32165                 if(this.mode == 'local'){
32166                     this.selectedIndex = -1;
32167                     if(forceAll){
32168                         this.store.clearFilter();
32169                     }else{
32170                         this.store.filter(this.displayField, q);
32171                     }
32172                     this.onLoad();
32173                 }else{
32174                     this.store.baseParams[this.queryParam] = q;
32175                     this.store.load({
32176                         params: this.getParams(q)
32177                     });
32178                     this.expand();
32179                 }
32180             }else{
32181                 this.selectedIndex = -1;
32182                 this.onLoad();   
32183             }
32184         }
32185     },
32186
32187     // private
32188     getParams : function(q){
32189         var p = {};
32190         //p[this.queryParam] = q;
32191         if(this.pageSize){
32192             p.start = 0;
32193             p.limit = this.pageSize;
32194         }
32195         return p;
32196     },
32197
32198     /**
32199      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
32200      */
32201     collapse : function(){
32202         
32203     },
32204
32205     // private
32206     collapseIf : function(e){
32207         
32208     },
32209
32210     /**
32211      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
32212      */
32213     expand : function(){
32214         
32215     } ,
32216
32217     // private
32218      
32219
32220     /** 
32221     * @cfg {Boolean} grow 
32222     * @hide 
32223     */
32224     /** 
32225     * @cfg {Number} growMin 
32226     * @hide 
32227     */
32228     /** 
32229     * @cfg {Number} growMax 
32230     * @hide 
32231     */
32232     /**
32233      * @hide
32234      * @method autoSize
32235      */
32236     
32237     setWidth : function()
32238     {
32239         
32240     },
32241     getResizeEl : function(){
32242         return this.el;
32243     }
32244 });//<script type="text/javasscript">
32245  
32246
32247 /**
32248  * @class Roo.DDView
32249  * A DnD enabled version of Roo.View.
32250  * @param {Element/String} container The Element in which to create the View.
32251  * @param {String} tpl The template string used to create the markup for each element of the View
32252  * @param {Object} config The configuration properties. These include all the config options of
32253  * {@link Roo.View} plus some specific to this class.<br>
32254  * <p>
32255  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
32256  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
32257  * <p>
32258  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
32259 .x-view-drag-insert-above {
32260         border-top:1px dotted #3366cc;
32261 }
32262 .x-view-drag-insert-below {
32263         border-bottom:1px dotted #3366cc;
32264 }
32265 </code></pre>
32266  * 
32267  */
32268  
32269 Roo.DDView = function(container, tpl, config) {
32270     Roo.DDView.superclass.constructor.apply(this, arguments);
32271     this.getEl().setStyle("outline", "0px none");
32272     this.getEl().unselectable();
32273     if (this.dragGroup) {
32274                 this.setDraggable(this.dragGroup.split(","));
32275     }
32276     if (this.dropGroup) {
32277                 this.setDroppable(this.dropGroup.split(","));
32278     }
32279     if (this.deletable) {
32280         this.setDeletable();
32281     }
32282     this.isDirtyFlag = false;
32283         this.addEvents({
32284                 "drop" : true
32285         });
32286 };
32287
32288 Roo.extend(Roo.DDView, Roo.View, {
32289 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
32290 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
32291 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
32292 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
32293
32294         isFormField: true,
32295
32296         reset: Roo.emptyFn,
32297         
32298         clearInvalid: Roo.form.Field.prototype.clearInvalid,
32299
32300         validate: function() {
32301                 return true;
32302         },
32303         
32304         destroy: function() {
32305                 this.purgeListeners();
32306                 this.getEl.removeAllListeners();
32307                 this.getEl().remove();
32308                 if (this.dragZone) {
32309                         if (this.dragZone.destroy) {
32310                                 this.dragZone.destroy();
32311                         }
32312                 }
32313                 if (this.dropZone) {
32314                         if (this.dropZone.destroy) {
32315                                 this.dropZone.destroy();
32316                         }
32317                 }
32318         },
32319
32320 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
32321         getName: function() {
32322                 return this.name;
32323         },
32324
32325 /**     Loads the View from a JSON string representing the Records to put into the Store. */
32326         setValue: function(v) {
32327                 if (!this.store) {
32328                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
32329                 }
32330                 var data = {};
32331                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
32332                 this.store.proxy = new Roo.data.MemoryProxy(data);
32333                 this.store.load();
32334         },
32335
32336 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
32337         getValue: function() {
32338                 var result = '(';
32339                 this.store.each(function(rec) {
32340                         result += rec.id + ',';
32341                 });
32342                 return result.substr(0, result.length - 1) + ')';
32343         },
32344         
32345         getIds: function() {
32346                 var i = 0, result = new Array(this.store.getCount());
32347                 this.store.each(function(rec) {
32348                         result[i++] = rec.id;
32349                 });
32350                 return result;
32351         },
32352         
32353         isDirty: function() {
32354                 return this.isDirtyFlag;
32355         },
32356
32357 /**
32358  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
32359  *      whole Element becomes the target, and this causes the drop gesture to append.
32360  */
32361     getTargetFromEvent : function(e) {
32362                 var target = e.getTarget();
32363                 while ((target !== null) && (target.parentNode != this.el.dom)) {
32364                 target = target.parentNode;
32365                 }
32366                 if (!target) {
32367                         target = this.el.dom.lastChild || this.el.dom;
32368                 }
32369                 return target;
32370     },
32371
32372 /**
32373  *      Create the drag data which consists of an object which has the property "ddel" as
32374  *      the drag proxy element. 
32375  */
32376     getDragData : function(e) {
32377         var target = this.findItemFromChild(e.getTarget());
32378                 if(target) {
32379                         this.handleSelection(e);
32380                         var selNodes = this.getSelectedNodes();
32381             var dragData = {
32382                 source: this,
32383                 copy: this.copy || (this.allowCopy && e.ctrlKey),
32384                 nodes: selNodes,
32385                 records: []
32386                         };
32387                         var selectedIndices = this.getSelectedIndexes();
32388                         for (var i = 0; i < selectedIndices.length; i++) {
32389                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
32390                         }
32391                         if (selNodes.length == 1) {
32392                                 dragData.ddel = target.cloneNode(true); // the div element
32393                         } else {
32394                                 var div = document.createElement('div'); // create the multi element drag "ghost"
32395                                 div.className = 'multi-proxy';
32396                                 for (var i = 0, len = selNodes.length; i < len; i++) {
32397                                         div.appendChild(selNodes[i].cloneNode(true));
32398                                 }
32399                                 dragData.ddel = div;
32400                         }
32401             //console.log(dragData)
32402             //console.log(dragData.ddel.innerHTML)
32403                         return dragData;
32404                 }
32405         //console.log('nodragData')
32406                 return false;
32407     },
32408     
32409 /**     Specify to which ddGroup items in this DDView may be dragged. */
32410     setDraggable: function(ddGroup) {
32411         if (ddGroup instanceof Array) {
32412                 Roo.each(ddGroup, this.setDraggable, this);
32413                 return;
32414         }
32415         if (this.dragZone) {
32416                 this.dragZone.addToGroup(ddGroup);
32417         } else {
32418                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
32419                                 containerScroll: true,
32420                                 ddGroup: ddGroup 
32421
32422                         });
32423 //                      Draggability implies selection. DragZone's mousedown selects the element.
32424                         if (!this.multiSelect) { this.singleSelect = true; }
32425
32426 //                      Wire the DragZone's handlers up to methods in *this*
32427                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
32428                 }
32429     },
32430
32431 /**     Specify from which ddGroup this DDView accepts drops. */
32432     setDroppable: function(ddGroup) {
32433         if (ddGroup instanceof Array) {
32434                 Roo.each(ddGroup, this.setDroppable, this);
32435                 return;
32436         }
32437         if (this.dropZone) {
32438                 this.dropZone.addToGroup(ddGroup);
32439         } else {
32440                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
32441                                 containerScroll: true,
32442                                 ddGroup: ddGroup
32443                         });
32444
32445 //                      Wire the DropZone's handlers up to methods in *this*
32446                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
32447                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
32448                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
32449                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
32450                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
32451                 }
32452     },
32453
32454 /**     Decide whether to drop above or below a View node. */
32455     getDropPoint : function(e, n, dd){
32456         if (n == this.el.dom) { return "above"; }
32457                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
32458                 var c = t + (b - t) / 2;
32459                 var y = Roo.lib.Event.getPageY(e);
32460                 if(y <= c) {
32461                         return "above";
32462                 }else{
32463                         return "below";
32464                 }
32465     },
32466
32467     onNodeEnter : function(n, dd, e, data){
32468                 return false;
32469     },
32470     
32471     onNodeOver : function(n, dd, e, data){
32472                 var pt = this.getDropPoint(e, n, dd);
32473                 // set the insert point style on the target node
32474                 var dragElClass = this.dropNotAllowed;
32475                 if (pt) {
32476                         var targetElClass;
32477                         if (pt == "above"){
32478                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
32479                                 targetElClass = "x-view-drag-insert-above";
32480                         } else {
32481                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
32482                                 targetElClass = "x-view-drag-insert-below";
32483                         }
32484                         if (this.lastInsertClass != targetElClass){
32485                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
32486                                 this.lastInsertClass = targetElClass;
32487                         }
32488                 }
32489                 return dragElClass;
32490         },
32491
32492     onNodeOut : function(n, dd, e, data){
32493                 this.removeDropIndicators(n);
32494     },
32495
32496     onNodeDrop : function(n, dd, e, data){
32497         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
32498                 return false;
32499         }
32500         var pt = this.getDropPoint(e, n, dd);
32501                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
32502                 if (pt == "below") { insertAt++; }
32503                 for (var i = 0; i < data.records.length; i++) {
32504                         var r = data.records[i];
32505                         var dup = this.store.getById(r.id);
32506                         if (dup && (dd != this.dragZone)) {
32507                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
32508                         } else {
32509                                 if (data.copy) {
32510                                         this.store.insert(insertAt++, r.copy());
32511                                 } else {
32512                                         data.source.isDirtyFlag = true;
32513                                         r.store.remove(r);
32514                                         this.store.insert(insertAt++, r);
32515                                 }
32516                                 this.isDirtyFlag = true;
32517                         }
32518                 }
32519                 this.dragZone.cachedTarget = null;
32520                 return true;
32521     },
32522
32523     removeDropIndicators : function(n){
32524                 if(n){
32525                         Roo.fly(n).removeClass([
32526                                 "x-view-drag-insert-above",
32527                                 "x-view-drag-insert-below"]);
32528                         this.lastInsertClass = "_noclass";
32529                 }
32530     },
32531
32532 /**
32533  *      Utility method. Add a delete option to the DDView's context menu.
32534  *      @param {String} imageUrl The URL of the "delete" icon image.
32535  */
32536         setDeletable: function(imageUrl) {
32537                 if (!this.singleSelect && !this.multiSelect) {
32538                         this.singleSelect = true;
32539                 }
32540                 var c = this.getContextMenu();
32541                 this.contextMenu.on("itemclick", function(item) {
32542                         switch (item.id) {
32543                                 case "delete":
32544                                         this.remove(this.getSelectedIndexes());
32545                                         break;
32546                         }
32547                 }, this);
32548                 this.contextMenu.add({
32549                         icon: imageUrl,
32550                         id: "delete",
32551                         text: 'Delete'
32552                 });
32553         },
32554         
32555 /**     Return the context menu for this DDView. */
32556         getContextMenu: function() {
32557                 if (!this.contextMenu) {
32558 //                      Create the View's context menu
32559                         this.contextMenu = new Roo.menu.Menu({
32560                                 id: this.id + "-contextmenu"
32561                         });
32562                         this.el.on("contextmenu", this.showContextMenu, this);
32563                 }
32564                 return this.contextMenu;
32565         },
32566         
32567         disableContextMenu: function() {
32568                 if (this.contextMenu) {
32569                         this.el.un("contextmenu", this.showContextMenu, this);
32570                 }
32571         },
32572
32573         showContextMenu: function(e, item) {
32574         item = this.findItemFromChild(e.getTarget());
32575                 if (item) {
32576                         e.stopEvent();
32577                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
32578                         this.contextMenu.showAt(e.getXY());
32579             }
32580     },
32581
32582 /**
32583  *      Remove {@link Roo.data.Record}s at the specified indices.
32584  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
32585  */
32586     remove: function(selectedIndices) {
32587                 selectedIndices = [].concat(selectedIndices);
32588                 for (var i = 0; i < selectedIndices.length; i++) {
32589                         var rec = this.store.getAt(selectedIndices[i]);
32590                         this.store.remove(rec);
32591                 }
32592     },
32593
32594 /**
32595  *      Double click fires the event, but also, if this is draggable, and there is only one other
32596  *      related DropZone, it transfers the selected node.
32597  */
32598     onDblClick : function(e){
32599         var item = this.findItemFromChild(e.getTarget());
32600         if(item){
32601             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
32602                 return false;
32603             }
32604             if (this.dragGroup) {
32605                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
32606                     while (targets.indexOf(this.dropZone) > -1) {
32607                             targets.remove(this.dropZone);
32608                                 }
32609                     if (targets.length == 1) {
32610                                         this.dragZone.cachedTarget = null;
32611                         var el = Roo.get(targets[0].getEl());
32612                         var box = el.getBox(true);
32613                         targets[0].onNodeDrop(el.dom, {
32614                                 target: el.dom,
32615                                 xy: [box.x, box.y + box.height - 1]
32616                         }, null, this.getDragData(e));
32617                     }
32618                 }
32619         }
32620     },
32621     
32622     handleSelection: function(e) {
32623                 this.dragZone.cachedTarget = null;
32624         var item = this.findItemFromChild(e.getTarget());
32625         if (!item) {
32626                 this.clearSelections(true);
32627                 return;
32628         }
32629                 if (item && (this.multiSelect || this.singleSelect)){
32630                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
32631                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
32632                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
32633                                 this.unselect(item);
32634                         } else {
32635                                 this.select(item, this.multiSelect && e.ctrlKey);
32636                                 this.lastSelection = item;
32637                         }
32638                 }
32639     },
32640
32641     onItemClick : function(item, index, e){
32642                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
32643                         return false;
32644                 }
32645                 return true;
32646     },
32647
32648     unselect : function(nodeInfo, suppressEvent){
32649                 var node = this.getNode(nodeInfo);
32650                 if(node && this.isSelected(node)){
32651                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
32652                                 Roo.fly(node).removeClass(this.selectedClass);
32653                                 this.selections.remove(node);
32654                                 if(!suppressEvent){
32655                                         this.fireEvent("selectionchange", this, this.selections);
32656                                 }
32657                         }
32658                 }
32659     }
32660 });
32661 /*
32662  * Based on:
32663  * Ext JS Library 1.1.1
32664  * Copyright(c) 2006-2007, Ext JS, LLC.
32665  *
32666  * Originally Released Under LGPL - original licence link has changed is not relivant.
32667  *
32668  * Fork - LGPL
32669  * <script type="text/javascript">
32670  */
32671  
32672 /**
32673  * @class Roo.LayoutManager
32674  * @extends Roo.util.Observable
32675  * Base class for layout managers.
32676  */
32677 Roo.LayoutManager = function(container, config){
32678     Roo.LayoutManager.superclass.constructor.call(this);
32679     this.el = Roo.get(container);
32680     // ie scrollbar fix
32681     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32682         document.body.scroll = "no";
32683     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32684         this.el.position('relative');
32685     }
32686     this.id = this.el.id;
32687     this.el.addClass("x-layout-container");
32688     /** false to disable window resize monitoring @type Boolean */
32689     this.monitorWindowResize = true;
32690     this.regions = {};
32691     this.addEvents({
32692         /**
32693          * @event layout
32694          * Fires when a layout is performed. 
32695          * @param {Roo.LayoutManager} this
32696          */
32697         "layout" : true,
32698         /**
32699          * @event regionresized
32700          * Fires when the user resizes a region. 
32701          * @param {Roo.LayoutRegion} region The resized region
32702          * @param {Number} newSize The new size (width for east/west, height for north/south)
32703          */
32704         "regionresized" : true,
32705         /**
32706          * @event regioncollapsed
32707          * Fires when a region is collapsed. 
32708          * @param {Roo.LayoutRegion} region The collapsed region
32709          */
32710         "regioncollapsed" : true,
32711         /**
32712          * @event regionexpanded
32713          * Fires when a region is expanded.  
32714          * @param {Roo.LayoutRegion} region The expanded region
32715          */
32716         "regionexpanded" : true
32717     });
32718     this.updating = false;
32719     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32720 };
32721
32722 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
32723     /**
32724      * Returns true if this layout is currently being updated
32725      * @return {Boolean}
32726      */
32727     isUpdating : function(){
32728         return this.updating; 
32729     },
32730     
32731     /**
32732      * Suspend the LayoutManager from doing auto-layouts while
32733      * making multiple add or remove calls
32734      */
32735     beginUpdate : function(){
32736         this.updating = true;    
32737     },
32738     
32739     /**
32740      * Restore auto-layouts and optionally disable the manager from performing a layout
32741      * @param {Boolean} noLayout true to disable a layout update 
32742      */
32743     endUpdate : function(noLayout){
32744         this.updating = false;
32745         if(!noLayout){
32746             this.layout();
32747         }    
32748     },
32749     
32750     layout: function(){
32751         
32752     },
32753     
32754     onRegionResized : function(region, newSize){
32755         this.fireEvent("regionresized", region, newSize);
32756         this.layout();
32757     },
32758     
32759     onRegionCollapsed : function(region){
32760         this.fireEvent("regioncollapsed", region);
32761     },
32762     
32763     onRegionExpanded : function(region){
32764         this.fireEvent("regionexpanded", region);
32765     },
32766         
32767     /**
32768      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32769      * performs box-model adjustments.
32770      * @return {Object} The size as an object {width: (the width), height: (the height)}
32771      */
32772     getViewSize : function(){
32773         var size;
32774         if(this.el.dom != document.body){
32775             size = this.el.getSize();
32776         }else{
32777             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32778         }
32779         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32780         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32781         return size;
32782     },
32783     
32784     /**
32785      * Returns the Element this layout is bound to.
32786      * @return {Roo.Element}
32787      */
32788     getEl : function(){
32789         return this.el;
32790     },
32791     
32792     /**
32793      * Returns the specified region.
32794      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32795      * @return {Roo.LayoutRegion}
32796      */
32797     getRegion : function(target){
32798         return this.regions[target.toLowerCase()];
32799     },
32800     
32801     onWindowResize : function(){
32802         if(this.monitorWindowResize){
32803             this.layout();
32804         }
32805     }
32806 });/*
32807  * Based on:
32808  * Ext JS Library 1.1.1
32809  * Copyright(c) 2006-2007, Ext JS, LLC.
32810  *
32811  * Originally Released Under LGPL - original licence link has changed is not relivant.
32812  *
32813  * Fork - LGPL
32814  * <script type="text/javascript">
32815  */
32816 /**
32817  * @class Roo.BorderLayout
32818  * @extends Roo.LayoutManager
32819  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32820  * please see: <br><br>
32821  * <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>
32822  * <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>
32823  * Example:
32824  <pre><code>
32825  var layout = new Roo.BorderLayout(document.body, {
32826     north: {
32827         initialSize: 25,
32828         titlebar: false
32829     },
32830     west: {
32831         split:true,
32832         initialSize: 200,
32833         minSize: 175,
32834         maxSize: 400,
32835         titlebar: true,
32836         collapsible: true
32837     },
32838     east: {
32839         split:true,
32840         initialSize: 202,
32841         minSize: 175,
32842         maxSize: 400,
32843         titlebar: true,
32844         collapsible: true
32845     },
32846     south: {
32847         split:true,
32848         initialSize: 100,
32849         minSize: 100,
32850         maxSize: 200,
32851         titlebar: true,
32852         collapsible: true
32853     },
32854     center: {
32855         titlebar: true,
32856         autoScroll:true,
32857         resizeTabs: true,
32858         minTabWidth: 50,
32859         preferredTabWidth: 150
32860     }
32861 });
32862
32863 // shorthand
32864 var CP = Roo.ContentPanel;
32865
32866 layout.beginUpdate();
32867 layout.add("north", new CP("north", "North"));
32868 layout.add("south", new CP("south", {title: "South", closable: true}));
32869 layout.add("west", new CP("west", {title: "West"}));
32870 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
32871 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
32872 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
32873 layout.getRegion("center").showPanel("center1");
32874 layout.endUpdate();
32875 </code></pre>
32876
32877 <b>The container the layout is rendered into can be either the body element or any other element.
32878 If it is not the body element, the container needs to either be an absolute positioned element,
32879 or you will need to add "position:relative" to the css of the container.  You will also need to specify
32880 the container size if it is not the body element.</b>
32881
32882 * @constructor
32883 * Create a new BorderLayout
32884 * @param {String/HTMLElement/Element} container The container this layout is bound to
32885 * @param {Object} config Configuration options
32886  */
32887 Roo.BorderLayout = function(container, config){
32888     config = config || {};
32889     Roo.BorderLayout.superclass.constructor.call(this, container, config);
32890     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
32891     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
32892         var target = this.factory.validRegions[i];
32893         if(config[target]){
32894             this.addRegion(target, config[target]);
32895         }
32896     }
32897 };
32898
32899 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
32900     /**
32901      * Creates and adds a new region if it doesn't already exist.
32902      * @param {String} target The target region key (north, south, east, west or center).
32903      * @param {Object} config The regions config object
32904      * @return {BorderLayoutRegion} The new region
32905      */
32906     addRegion : function(target, config){
32907         if(!this.regions[target]){
32908             var r = this.factory.create(target, this, config);
32909             this.bindRegion(target, r);
32910         }
32911         return this.regions[target];
32912     },
32913
32914     // private (kinda)
32915     bindRegion : function(name, r){
32916         this.regions[name] = r;
32917         r.on("visibilitychange", this.layout, this);
32918         r.on("paneladded", this.layout, this);
32919         r.on("panelremoved", this.layout, this);
32920         r.on("invalidated", this.layout, this);
32921         r.on("resized", this.onRegionResized, this);
32922         r.on("collapsed", this.onRegionCollapsed, this);
32923         r.on("expanded", this.onRegionExpanded, this);
32924     },
32925
32926     /**
32927      * Performs a layout update.
32928      */
32929     layout : function(){
32930         if(this.updating) return;
32931         var size = this.getViewSize();
32932         var w = size.width;
32933         var h = size.height;
32934         var centerW = w;
32935         var centerH = h;
32936         var centerY = 0;
32937         var centerX = 0;
32938         //var x = 0, y = 0;
32939
32940         var rs = this.regions;
32941         var north = rs["north"];
32942         var south = rs["south"]; 
32943         var west = rs["west"];
32944         var east = rs["east"];
32945         var center = rs["center"];
32946         //if(this.hideOnLayout){ // not supported anymore
32947             //c.el.setStyle("display", "none");
32948         //}
32949         if(north && north.isVisible()){
32950             var b = north.getBox();
32951             var m = north.getMargins();
32952             b.width = w - (m.left+m.right);
32953             b.x = m.left;
32954             b.y = m.top;
32955             centerY = b.height + b.y + m.bottom;
32956             centerH -= centerY;
32957             north.updateBox(this.safeBox(b));
32958         }
32959         if(south && south.isVisible()){
32960             var b = south.getBox();
32961             var m = south.getMargins();
32962             b.width = w - (m.left+m.right);
32963             b.x = m.left;
32964             var totalHeight = (b.height + m.top + m.bottom);
32965             b.y = h - totalHeight + m.top;
32966             centerH -= totalHeight;
32967             south.updateBox(this.safeBox(b));
32968         }
32969         if(west && west.isVisible()){
32970             var b = west.getBox();
32971             var m = west.getMargins();
32972             b.height = centerH - (m.top+m.bottom);
32973             b.x = m.left;
32974             b.y = centerY + m.top;
32975             var totalWidth = (b.width + m.left + m.right);
32976             centerX += totalWidth;
32977             centerW -= totalWidth;
32978             west.updateBox(this.safeBox(b));
32979         }
32980         if(east && east.isVisible()){
32981             var b = east.getBox();
32982             var m = east.getMargins();
32983             b.height = centerH - (m.top+m.bottom);
32984             var totalWidth = (b.width + m.left + m.right);
32985             b.x = w - totalWidth + m.left;
32986             b.y = centerY + m.top;
32987             centerW -= totalWidth;
32988             east.updateBox(this.safeBox(b));
32989         }
32990         if(center){
32991             var m = center.getMargins();
32992             var centerBox = {
32993                 x: centerX + m.left,
32994                 y: centerY + m.top,
32995                 width: centerW - (m.left+m.right),
32996                 height: centerH - (m.top+m.bottom)
32997             };
32998             //if(this.hideOnLayout){
32999                 //center.el.setStyle("display", "block");
33000             //}
33001             center.updateBox(this.safeBox(centerBox));
33002         }
33003         this.el.repaint();
33004         this.fireEvent("layout", this);
33005     },
33006
33007     // private
33008     safeBox : function(box){
33009         box.width = Math.max(0, box.width);
33010         box.height = Math.max(0, box.height);
33011         return box;
33012     },
33013
33014     /**
33015      * Adds a ContentPanel (or subclass) to this layout.
33016      * @param {String} target The target region key (north, south, east, west or center).
33017      * @param {Roo.ContentPanel} panel The panel to add
33018      * @return {Roo.ContentPanel} The added panel
33019      */
33020     add : function(target, panel){
33021          
33022         target = target.toLowerCase();
33023         return this.regions[target].add(panel);
33024     },
33025
33026     /**
33027      * Remove a ContentPanel (or subclass) to this layout.
33028      * @param {String} target The target region key (north, south, east, west or center).
33029      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
33030      * @return {Roo.ContentPanel} The removed panel
33031      */
33032     remove : function(target, panel){
33033         target = target.toLowerCase();
33034         return this.regions[target].remove(panel);
33035     },
33036
33037     /**
33038      * Searches all regions for a panel with the specified id
33039      * @param {String} panelId
33040      * @return {Roo.ContentPanel} The panel or null if it wasn't found
33041      */
33042     findPanel : function(panelId){
33043         var rs = this.regions;
33044         for(var target in rs){
33045             if(typeof rs[target] != "function"){
33046                 var p = rs[target].getPanel(panelId);
33047                 if(p){
33048                     return p;
33049                 }
33050             }
33051         }
33052         return null;
33053     },
33054
33055     /**
33056      * Searches all regions for a panel with the specified id and activates (shows) it.
33057      * @param {String/ContentPanel} panelId The panels id or the panel itself
33058      * @return {Roo.ContentPanel} The shown panel or null
33059      */
33060     showPanel : function(panelId) {
33061       var rs = this.regions;
33062       for(var target in rs){
33063          var r = rs[target];
33064          if(typeof r != "function"){
33065             if(r.hasPanel(panelId)){
33066                return r.showPanel(panelId);
33067             }
33068          }
33069       }
33070       return null;
33071    },
33072
33073    /**
33074      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
33075      * @param {Roo.state.Provider} provider (optional) An alternate state provider
33076      */
33077     restoreState : function(provider){
33078         if(!provider){
33079             provider = Roo.state.Manager;
33080         }
33081         var sm = new Roo.LayoutStateManager();
33082         sm.init(this, provider);
33083     },
33084
33085     /**
33086      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
33087      * object should contain properties for each region to add ContentPanels to, and each property's value should be
33088      * a valid ContentPanel config object.  Example:
33089      * <pre><code>
33090 // Create the main layout
33091 var layout = new Roo.BorderLayout('main-ct', {
33092     west: {
33093         split:true,
33094         minSize: 175,
33095         titlebar: true
33096     },
33097     center: {
33098         title:'Components'
33099     }
33100 }, 'main-ct');
33101
33102 // Create and add multiple ContentPanels at once via configs
33103 layout.batchAdd({
33104    west: {
33105        id: 'source-files',
33106        autoCreate:true,
33107        title:'Ext Source Files',
33108        autoScroll:true,
33109        fitToFrame:true
33110    },
33111    center : {
33112        el: cview,
33113        autoScroll:true,
33114        fitToFrame:true,
33115        toolbar: tb,
33116        resizeEl:'cbody'
33117    }
33118 });
33119 </code></pre>
33120      * @param {Object} regions An object containing ContentPanel configs by region name
33121      */
33122     batchAdd : function(regions){
33123         this.beginUpdate();
33124         for(var rname in regions){
33125             var lr = this.regions[rname];
33126             if(lr){
33127                 this.addTypedPanels(lr, regions[rname]);
33128             }
33129         }
33130         this.endUpdate();
33131     },
33132
33133     // private
33134     addTypedPanels : function(lr, ps){
33135         if(typeof ps == 'string'){
33136             lr.add(new Roo.ContentPanel(ps));
33137         }
33138         else if(ps instanceof Array){
33139             for(var i =0, len = ps.length; i < len; i++){
33140                 this.addTypedPanels(lr, ps[i]);
33141             }
33142         }
33143         else if(!ps.events){ // raw config?
33144             var el = ps.el;
33145             delete ps.el; // prevent conflict
33146             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
33147         }
33148         else {  // panel object assumed!
33149             lr.add(ps);
33150         }
33151     },
33152     /**
33153      * Adds a xtype elements to the layout.
33154      * <pre><code>
33155
33156 layout.addxtype({
33157        xtype : 'ContentPanel',
33158        region: 'west',
33159        items: [ .... ]
33160    }
33161 );
33162
33163 layout.addxtype({
33164         xtype : 'NestedLayoutPanel',
33165         region: 'west',
33166         layout: {
33167            center: { },
33168            west: { }   
33169         },
33170         items : [ ... list of content panels or nested layout panels.. ]
33171    }
33172 );
33173 </code></pre>
33174      * @param {Object} cfg Xtype definition of item to add.
33175      */
33176     addxtype : function(cfg)
33177     {
33178         // basically accepts a pannel...
33179         // can accept a layout region..!?!?
33180         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
33181         
33182         if (!cfg.xtype.match(/Panel$/)) {
33183             return false;
33184         }
33185         var ret = false;
33186         
33187         if (typeof(cfg.region) == 'undefined') {
33188             Roo.log("Failed to add Panel, region was not set");
33189             Roo.log(cfg);
33190             return false;
33191         }
33192         var region = cfg.region;
33193         delete cfg.region;
33194         
33195           
33196         var xitems = [];
33197         if (cfg.items) {
33198             xitems = cfg.items;
33199             delete cfg.items;
33200         }
33201         var nb = false;
33202         
33203         switch(cfg.xtype) 
33204         {
33205             case 'ContentPanel':  // ContentPanel (el, cfg)
33206             case 'ScrollPanel':  // ContentPanel (el, cfg)
33207             case 'ViewPanel': 
33208                 if(cfg.autoCreate) {
33209                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33210                 } else {
33211                     var el = this.el.createChild();
33212                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
33213                 }
33214                 
33215                 this.add(region, ret);
33216                 break;
33217             
33218             
33219             case 'TreePanel': // our new panel!
33220                 cfg.el = this.el.createChild();
33221                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33222                 this.add(region, ret);
33223                 break;
33224             
33225             case 'NestedLayoutPanel': 
33226                 // create a new Layout (which is  a Border Layout...
33227                 var el = this.el.createChild();
33228                 var clayout = cfg.layout;
33229                 delete cfg.layout;
33230                 clayout.items   = clayout.items  || [];
33231                 // replace this exitems with the clayout ones..
33232                 xitems = clayout.items;
33233                  
33234                 
33235                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
33236                     cfg.background = false;
33237                 }
33238                 var layout = new Roo.BorderLayout(el, clayout);
33239                 
33240                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
33241                 //console.log('adding nested layout panel '  + cfg.toSource());
33242                 this.add(region, ret);
33243                 nb = {}; /// find first...
33244                 break;
33245                 
33246             case 'GridPanel': 
33247             
33248                 // needs grid and region
33249                 
33250                 //var el = this.getRegion(region).el.createChild();
33251                 var el = this.el.createChild();
33252                 // create the grid first...
33253                 
33254                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
33255                 delete cfg.grid;
33256                 if (region == 'center' && this.active ) {
33257                     cfg.background = false;
33258                 }
33259                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
33260                 
33261                 this.add(region, ret);
33262                 if (cfg.background) {
33263                     ret.on('activate', function(gp) {
33264                         if (!gp.grid.rendered) {
33265                             gp.grid.render();
33266                         }
33267                     });
33268                 } else {
33269                     grid.render();
33270                 }
33271                 break;
33272            
33273            
33274            
33275                 
33276                 
33277                 
33278             default:
33279                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
33280                     
33281                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33282                     this.add(region, ret);
33283                 } else {
33284                 
33285                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
33286                     return null;
33287                 }
33288                 
33289              // GridPanel (grid, cfg)
33290             
33291         }
33292         this.beginUpdate();
33293         // add children..
33294         var region = '';
33295         var abn = {};
33296         Roo.each(xitems, function(i)  {
33297             region = nb && i.region ? i.region : false;
33298             
33299             var add = ret.addxtype(i);
33300            
33301             if (region) {
33302                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
33303                 if (!i.background) {
33304                     abn[region] = nb[region] ;
33305                 }
33306             }
33307             
33308         });
33309         this.endUpdate();
33310
33311         // make the last non-background panel active..
33312         //if (nb) { Roo.log(abn); }
33313         if (nb) {
33314             
33315             for(var r in abn) {
33316                 region = this.getRegion(r);
33317                 if (region) {
33318                     // tried using nb[r], but it does not work..
33319                      
33320                     region.showPanel(abn[r]);
33321                    
33322                 }
33323             }
33324         }
33325         return ret;
33326         
33327     }
33328 });
33329
33330 /**
33331  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
33332  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
33333  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
33334  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
33335  * <pre><code>
33336 // shorthand
33337 var CP = Roo.ContentPanel;
33338
33339 var layout = Roo.BorderLayout.create({
33340     north: {
33341         initialSize: 25,
33342         titlebar: false,
33343         panels: [new CP("north", "North")]
33344     },
33345     west: {
33346         split:true,
33347         initialSize: 200,
33348         minSize: 175,
33349         maxSize: 400,
33350         titlebar: true,
33351         collapsible: true,
33352         panels: [new CP("west", {title: "West"})]
33353     },
33354     east: {
33355         split:true,
33356         initialSize: 202,
33357         minSize: 175,
33358         maxSize: 400,
33359         titlebar: true,
33360         collapsible: true,
33361         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
33362     },
33363     south: {
33364         split:true,
33365         initialSize: 100,
33366         minSize: 100,
33367         maxSize: 200,
33368         titlebar: true,
33369         collapsible: true,
33370         panels: [new CP("south", {title: "South", closable: true})]
33371     },
33372     center: {
33373         titlebar: true,
33374         autoScroll:true,
33375         resizeTabs: true,
33376         minTabWidth: 50,
33377         preferredTabWidth: 150,
33378         panels: [
33379             new CP("center1", {title: "Close Me", closable: true}),
33380             new CP("center2", {title: "Center Panel", closable: false})
33381         ]
33382     }
33383 }, document.body);
33384
33385 layout.getRegion("center").showPanel("center1");
33386 </code></pre>
33387  * @param config
33388  * @param targetEl
33389  */
33390 Roo.BorderLayout.create = function(config, targetEl){
33391     var layout = new Roo.BorderLayout(targetEl || document.body, config);
33392     layout.beginUpdate();
33393     var regions = Roo.BorderLayout.RegionFactory.validRegions;
33394     for(var j = 0, jlen = regions.length; j < jlen; j++){
33395         var lr = regions[j];
33396         if(layout.regions[lr] && config[lr].panels){
33397             var r = layout.regions[lr];
33398             var ps = config[lr].panels;
33399             layout.addTypedPanels(r, ps);
33400         }
33401     }
33402     layout.endUpdate();
33403     return layout;
33404 };
33405
33406 // private
33407 Roo.BorderLayout.RegionFactory = {
33408     // private
33409     validRegions : ["north","south","east","west","center"],
33410
33411     // private
33412     create : function(target, mgr, config){
33413         target = target.toLowerCase();
33414         if(config.lightweight || config.basic){
33415             return new Roo.BasicLayoutRegion(mgr, config, target);
33416         }
33417         switch(target){
33418             case "north":
33419                 return new Roo.NorthLayoutRegion(mgr, config);
33420             case "south":
33421                 return new Roo.SouthLayoutRegion(mgr, config);
33422             case "east":
33423                 return new Roo.EastLayoutRegion(mgr, config);
33424             case "west":
33425                 return new Roo.WestLayoutRegion(mgr, config);
33426             case "center":
33427                 return new Roo.CenterLayoutRegion(mgr, config);
33428         }
33429         throw 'Layout region "'+target+'" not supported.';
33430     }
33431 };/*
33432  * Based on:
33433  * Ext JS Library 1.1.1
33434  * Copyright(c) 2006-2007, Ext JS, LLC.
33435  *
33436  * Originally Released Under LGPL - original licence link has changed is not relivant.
33437  *
33438  * Fork - LGPL
33439  * <script type="text/javascript">
33440  */
33441  
33442 /**
33443  * @class Roo.BasicLayoutRegion
33444  * @extends Roo.util.Observable
33445  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
33446  * and does not have a titlebar, tabs or any other features. All it does is size and position 
33447  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
33448  */
33449 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
33450     this.mgr = mgr;
33451     this.position  = pos;
33452     this.events = {
33453         /**
33454          * @scope Roo.BasicLayoutRegion
33455          */
33456         
33457         /**
33458          * @event beforeremove
33459          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
33460          * @param {Roo.LayoutRegion} this
33461          * @param {Roo.ContentPanel} panel The panel
33462          * @param {Object} e The cancel event object
33463          */
33464         "beforeremove" : true,
33465         /**
33466          * @event invalidated
33467          * Fires when the layout for this region is changed.
33468          * @param {Roo.LayoutRegion} this
33469          */
33470         "invalidated" : true,
33471         /**
33472          * @event visibilitychange
33473          * Fires when this region is shown or hidden 
33474          * @param {Roo.LayoutRegion} this
33475          * @param {Boolean} visibility true or false
33476          */
33477         "visibilitychange" : true,
33478         /**
33479          * @event paneladded
33480          * Fires when a panel is added. 
33481          * @param {Roo.LayoutRegion} this
33482          * @param {Roo.ContentPanel} panel The panel
33483          */
33484         "paneladded" : true,
33485         /**
33486          * @event panelremoved
33487          * Fires when a panel is removed. 
33488          * @param {Roo.LayoutRegion} this
33489          * @param {Roo.ContentPanel} panel The panel
33490          */
33491         "panelremoved" : true,
33492         /**
33493          * @event collapsed
33494          * Fires when this region is collapsed.
33495          * @param {Roo.LayoutRegion} this
33496          */
33497         "collapsed" : true,
33498         /**
33499          * @event expanded
33500          * Fires when this region is expanded.
33501          * @param {Roo.LayoutRegion} this
33502          */
33503         "expanded" : true,
33504         /**
33505          * @event slideshow
33506          * Fires when this region is slid into view.
33507          * @param {Roo.LayoutRegion} this
33508          */
33509         "slideshow" : true,
33510         /**
33511          * @event slidehide
33512          * Fires when this region slides out of view. 
33513          * @param {Roo.LayoutRegion} this
33514          */
33515         "slidehide" : true,
33516         /**
33517          * @event panelactivated
33518          * Fires when a panel is activated. 
33519          * @param {Roo.LayoutRegion} this
33520          * @param {Roo.ContentPanel} panel The activated panel
33521          */
33522         "panelactivated" : true,
33523         /**
33524          * @event resized
33525          * Fires when the user resizes this region. 
33526          * @param {Roo.LayoutRegion} this
33527          * @param {Number} newSize The new size (width for east/west, height for north/south)
33528          */
33529         "resized" : true
33530     };
33531     /** A collection of panels in this region. @type Roo.util.MixedCollection */
33532     this.panels = new Roo.util.MixedCollection();
33533     this.panels.getKey = this.getPanelId.createDelegate(this);
33534     this.box = null;
33535     this.activePanel = null;
33536     // ensure listeners are added...
33537     
33538     if (config.listeners || config.events) {
33539         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
33540             listeners : config.listeners || {},
33541             events : config.events || {}
33542         });
33543     }
33544     
33545     if(skipConfig !== true){
33546         this.applyConfig(config);
33547     }
33548 };
33549
33550 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
33551     getPanelId : function(p){
33552         return p.getId();
33553     },
33554     
33555     applyConfig : function(config){
33556         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33557         this.config = config;
33558         
33559     },
33560     
33561     /**
33562      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
33563      * the width, for horizontal (north, south) the height.
33564      * @param {Number} newSize The new width or height
33565      */
33566     resizeTo : function(newSize){
33567         var el = this.el ? this.el :
33568                  (this.activePanel ? this.activePanel.getEl() : null);
33569         if(el){
33570             switch(this.position){
33571                 case "east":
33572                 case "west":
33573                     el.setWidth(newSize);
33574                     this.fireEvent("resized", this, newSize);
33575                 break;
33576                 case "north":
33577                 case "south":
33578                     el.setHeight(newSize);
33579                     this.fireEvent("resized", this, newSize);
33580                 break;                
33581             }
33582         }
33583     },
33584     
33585     getBox : function(){
33586         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33587     },
33588     
33589     getMargins : function(){
33590         return this.margins;
33591     },
33592     
33593     updateBox : function(box){
33594         this.box = box;
33595         var el = this.activePanel.getEl();
33596         el.dom.style.left = box.x + "px";
33597         el.dom.style.top = box.y + "px";
33598         this.activePanel.setSize(box.width, box.height);
33599     },
33600     
33601     /**
33602      * Returns the container element for this region.
33603      * @return {Roo.Element}
33604      */
33605     getEl : function(){
33606         return this.activePanel;
33607     },
33608     
33609     /**
33610      * Returns true if this region is currently visible.
33611      * @return {Boolean}
33612      */
33613     isVisible : function(){
33614         return this.activePanel ? true : false;
33615     },
33616     
33617     setActivePanel : function(panel){
33618         panel = this.getPanel(panel);
33619         if(this.activePanel && this.activePanel != panel){
33620             this.activePanel.setActiveState(false);
33621             this.activePanel.getEl().setLeftTop(-10000,-10000);
33622         }
33623         this.activePanel = panel;
33624         panel.setActiveState(true);
33625         if(this.box){
33626             panel.setSize(this.box.width, this.box.height);
33627         }
33628         this.fireEvent("panelactivated", this, panel);
33629         this.fireEvent("invalidated");
33630     },
33631     
33632     /**
33633      * Show the specified panel.
33634      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33635      * @return {Roo.ContentPanel} The shown panel or null
33636      */
33637     showPanel : function(panel){
33638         if(panel = this.getPanel(panel)){
33639             this.setActivePanel(panel);
33640         }
33641         return panel;
33642     },
33643     
33644     /**
33645      * Get the active panel for this region.
33646      * @return {Roo.ContentPanel} The active panel or null
33647      */
33648     getActivePanel : function(){
33649         return this.activePanel;
33650     },
33651     
33652     /**
33653      * Add the passed ContentPanel(s)
33654      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33655      * @return {Roo.ContentPanel} The panel added (if only one was added)
33656      */
33657     add : function(panel){
33658         if(arguments.length > 1){
33659             for(var i = 0, len = arguments.length; i < len; i++) {
33660                 this.add(arguments[i]);
33661             }
33662             return null;
33663         }
33664         if(this.hasPanel(panel)){
33665             this.showPanel(panel);
33666             return panel;
33667         }
33668         var el = panel.getEl();
33669         if(el.dom.parentNode != this.mgr.el.dom){
33670             this.mgr.el.dom.appendChild(el.dom);
33671         }
33672         if(panel.setRegion){
33673             panel.setRegion(this);
33674         }
33675         this.panels.add(panel);
33676         el.setStyle("position", "absolute");
33677         if(!panel.background){
33678             this.setActivePanel(panel);
33679             if(this.config.initialSize && this.panels.getCount()==1){
33680                 this.resizeTo(this.config.initialSize);
33681             }
33682         }
33683         this.fireEvent("paneladded", this, panel);
33684         return panel;
33685     },
33686     
33687     /**
33688      * Returns true if the panel is in this region.
33689      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33690      * @return {Boolean}
33691      */
33692     hasPanel : function(panel){
33693         if(typeof panel == "object"){ // must be panel obj
33694             panel = panel.getId();
33695         }
33696         return this.getPanel(panel) ? true : false;
33697     },
33698     
33699     /**
33700      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33701      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33702      * @param {Boolean} preservePanel Overrides the config preservePanel option
33703      * @return {Roo.ContentPanel} The panel that was removed
33704      */
33705     remove : function(panel, preservePanel){
33706         panel = this.getPanel(panel);
33707         if(!panel){
33708             return null;
33709         }
33710         var e = {};
33711         this.fireEvent("beforeremove", this, panel, e);
33712         if(e.cancel === true){
33713             return null;
33714         }
33715         var panelId = panel.getId();
33716         this.panels.removeKey(panelId);
33717         return panel;
33718     },
33719     
33720     /**
33721      * Returns the panel specified or null if it's not in this region.
33722      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33723      * @return {Roo.ContentPanel}
33724      */
33725     getPanel : function(id){
33726         if(typeof id == "object"){ // must be panel obj
33727             return id;
33728         }
33729         return this.panels.get(id);
33730     },
33731     
33732     /**
33733      * Returns this regions position (north/south/east/west/center).
33734      * @return {String} 
33735      */
33736     getPosition: function(){
33737         return this.position;    
33738     }
33739 });/*
33740  * Based on:
33741  * Ext JS Library 1.1.1
33742  * Copyright(c) 2006-2007, Ext JS, LLC.
33743  *
33744  * Originally Released Under LGPL - original licence link has changed is not relivant.
33745  *
33746  * Fork - LGPL
33747  * <script type="text/javascript">
33748  */
33749  
33750 /**
33751  * @class Roo.LayoutRegion
33752  * @extends Roo.BasicLayoutRegion
33753  * This class represents a region in a layout manager.
33754  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
33755  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
33756  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
33757  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33758  * @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})
33759  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
33760  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
33761  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
33762  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
33763  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
33764  * @cfg {String}    title           The title for the region (overrides panel titles)
33765  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
33766  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33767  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
33768  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33769  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
33770  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33771  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
33772  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
33773  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
33774  * @cfg {Boolean}   showPin         True to show a pin button
33775  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
33776  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
33777  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
33778  * @cfg {Number}    width           For East/West panels
33779  * @cfg {Number}    height          For North/South panels
33780  * @cfg {Boolean}   split           To show the splitter
33781  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
33782  */
33783 Roo.LayoutRegion = function(mgr, config, pos){
33784     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
33785     var dh = Roo.DomHelper;
33786     /** This region's container element 
33787     * @type Roo.Element */
33788     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
33789     /** This region's title element 
33790     * @type Roo.Element */
33791
33792     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
33793         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
33794         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
33795     ]}, true);
33796     this.titleEl.enableDisplayMode();
33797     /** This region's title text element 
33798     * @type HTMLElement */
33799     this.titleTextEl = this.titleEl.dom.firstChild;
33800     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33801     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
33802     this.closeBtn.enableDisplayMode();
33803     this.closeBtn.on("click", this.closeClicked, this);
33804     this.closeBtn.hide();
33805
33806     this.createBody(config);
33807     this.visible = true;
33808     this.collapsed = false;
33809
33810     if(config.hideWhenEmpty){
33811         this.hide();
33812         this.on("paneladded", this.validateVisibility, this);
33813         this.on("panelremoved", this.validateVisibility, this);
33814     }
33815     this.applyConfig(config);
33816 };
33817
33818 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
33819
33820     createBody : function(){
33821         /** This region's body element 
33822         * @type Roo.Element */
33823         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
33824     },
33825
33826     applyConfig : function(c){
33827         if(c.collapsible && this.position != "center" && !this.collapsedEl){
33828             var dh = Roo.DomHelper;
33829             if(c.titlebar !== false){
33830                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
33831                 this.collapseBtn.on("click", this.collapse, this);
33832                 this.collapseBtn.enableDisplayMode();
33833
33834                 if(c.showPin === true || this.showPin){
33835                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
33836                     this.stickBtn.enableDisplayMode();
33837                     this.stickBtn.on("click", this.expand, this);
33838                     this.stickBtn.hide();
33839                 }
33840             }
33841             /** This region's collapsed element
33842             * @type Roo.Element */
33843             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33844                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33845             ]}, true);
33846             if(c.floatable !== false){
33847                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33848                this.collapsedEl.on("click", this.collapseClick, this);
33849             }
33850
33851             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33852                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33853                    id: "message", unselectable: "on", style:{"float":"left"}});
33854                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33855              }
33856             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33857             this.expandBtn.on("click", this.expand, this);
33858         }
33859         if(this.collapseBtn){
33860             this.collapseBtn.setVisible(c.collapsible == true);
33861         }
33862         this.cmargins = c.cmargins || this.cmargins ||
33863                          (this.position == "west" || this.position == "east" ?
33864                              {top: 0, left: 2, right:2, bottom: 0} :
33865                              {top: 2, left: 0, right:0, bottom: 2});
33866         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33867         this.bottomTabs = c.tabPosition != "top";
33868         this.autoScroll = c.autoScroll || false;
33869         if(this.autoScroll){
33870             this.bodyEl.setStyle("overflow", "auto");
33871         }else{
33872             this.bodyEl.setStyle("overflow", "hidden");
33873         }
33874         //if(c.titlebar !== false){
33875             if((!c.titlebar && !c.title) || c.titlebar === false){
33876                 this.titleEl.hide();
33877             }else{
33878                 this.titleEl.show();
33879                 if(c.title){
33880                     this.titleTextEl.innerHTML = c.title;
33881                 }
33882             }
33883         //}
33884         this.duration = c.duration || .30;
33885         this.slideDuration = c.slideDuration || .45;
33886         this.config = c;
33887         if(c.collapsed){
33888             this.collapse(true);
33889         }
33890         if(c.hidden){
33891             this.hide();
33892         }
33893     },
33894     /**
33895      * Returns true if this region is currently visible.
33896      * @return {Boolean}
33897      */
33898     isVisible : function(){
33899         return this.visible;
33900     },
33901
33902     /**
33903      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33904      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
33905      */
33906     setCollapsedTitle : function(title){
33907         title = title || "&#160;";
33908         if(this.collapsedTitleTextEl){
33909             this.collapsedTitleTextEl.innerHTML = title;
33910         }
33911     },
33912
33913     getBox : function(){
33914         var b;
33915         if(!this.collapsed){
33916             b = this.el.getBox(false, true);
33917         }else{
33918             b = this.collapsedEl.getBox(false, true);
33919         }
33920         return b;
33921     },
33922
33923     getMargins : function(){
33924         return this.collapsed ? this.cmargins : this.margins;
33925     },
33926
33927     highlight : function(){
33928         this.el.addClass("x-layout-panel-dragover");
33929     },
33930
33931     unhighlight : function(){
33932         this.el.removeClass("x-layout-panel-dragover");
33933     },
33934
33935     updateBox : function(box){
33936         this.box = box;
33937         if(!this.collapsed){
33938             this.el.dom.style.left = box.x + "px";
33939             this.el.dom.style.top = box.y + "px";
33940             this.updateBody(box.width, box.height);
33941         }else{
33942             this.collapsedEl.dom.style.left = box.x + "px";
33943             this.collapsedEl.dom.style.top = box.y + "px";
33944             this.collapsedEl.setSize(box.width, box.height);
33945         }
33946         if(this.tabs){
33947             this.tabs.autoSizeTabs();
33948         }
33949     },
33950
33951     updateBody : function(w, h){
33952         if(w !== null){
33953             this.el.setWidth(w);
33954             w -= this.el.getBorderWidth("rl");
33955             if(this.config.adjustments){
33956                 w += this.config.adjustments[0];
33957             }
33958         }
33959         if(h !== null){
33960             this.el.setHeight(h);
33961             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33962             h -= this.el.getBorderWidth("tb");
33963             if(this.config.adjustments){
33964                 h += this.config.adjustments[1];
33965             }
33966             this.bodyEl.setHeight(h);
33967             if(this.tabs){
33968                 h = this.tabs.syncHeight(h);
33969             }
33970         }
33971         if(this.panelSize){
33972             w = w !== null ? w : this.panelSize.width;
33973             h = h !== null ? h : this.panelSize.height;
33974         }
33975         if(this.activePanel){
33976             var el = this.activePanel.getEl();
33977             w = w !== null ? w : el.getWidth();
33978             h = h !== null ? h : el.getHeight();
33979             this.panelSize = {width: w, height: h};
33980             this.activePanel.setSize(w, h);
33981         }
33982         if(Roo.isIE && this.tabs){
33983             this.tabs.el.repaint();
33984         }
33985     },
33986
33987     /**
33988      * Returns the container element for this region.
33989      * @return {Roo.Element}
33990      */
33991     getEl : function(){
33992         return this.el;
33993     },
33994
33995     /**
33996      * Hides this region.
33997      */
33998     hide : function(){
33999         if(!this.collapsed){
34000             this.el.dom.style.left = "-2000px";
34001             this.el.hide();
34002         }else{
34003             this.collapsedEl.dom.style.left = "-2000px";
34004             this.collapsedEl.hide();
34005         }
34006         this.visible = false;
34007         this.fireEvent("visibilitychange", this, false);
34008     },
34009
34010     /**
34011      * Shows this region if it was previously hidden.
34012      */
34013     show : function(){
34014         if(!this.collapsed){
34015             this.el.show();
34016         }else{
34017             this.collapsedEl.show();
34018         }
34019         this.visible = true;
34020         this.fireEvent("visibilitychange", this, true);
34021     },
34022
34023     closeClicked : function(){
34024         if(this.activePanel){
34025             this.remove(this.activePanel);
34026         }
34027     },
34028
34029     collapseClick : function(e){
34030         if(this.isSlid){
34031            e.stopPropagation();
34032            this.slideIn();
34033         }else{
34034            e.stopPropagation();
34035            this.slideOut();
34036         }
34037     },
34038
34039     /**
34040      * Collapses this region.
34041      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
34042      */
34043     collapse : function(skipAnim){
34044         if(this.collapsed) return;
34045         this.collapsed = true;
34046         if(this.split){
34047             this.split.el.hide();
34048         }
34049         if(this.config.animate && skipAnim !== true){
34050             this.fireEvent("invalidated", this);
34051             this.animateCollapse();
34052         }else{
34053             this.el.setLocation(-20000,-20000);
34054             this.el.hide();
34055             this.collapsedEl.show();
34056             this.fireEvent("collapsed", this);
34057             this.fireEvent("invalidated", this);
34058         }
34059     },
34060
34061     animateCollapse : function(){
34062         // overridden
34063     },
34064
34065     /**
34066      * Expands this region if it was previously collapsed.
34067      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
34068      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
34069      */
34070     expand : function(e, skipAnim){
34071         if(e) e.stopPropagation();
34072         if(!this.collapsed || this.el.hasActiveFx()) return;
34073         if(this.isSlid){
34074             this.afterSlideIn();
34075             skipAnim = true;
34076         }
34077         this.collapsed = false;
34078         if(this.config.animate && skipAnim !== true){
34079             this.animateExpand();
34080         }else{
34081             this.el.show();
34082             if(this.split){
34083                 this.split.el.show();
34084             }
34085             this.collapsedEl.setLocation(-2000,-2000);
34086             this.collapsedEl.hide();
34087             this.fireEvent("invalidated", this);
34088             this.fireEvent("expanded", this);
34089         }
34090     },
34091
34092     animateExpand : function(){
34093         // overridden
34094     },
34095
34096     initTabs : function()
34097     {
34098         this.bodyEl.setStyle("overflow", "hidden");
34099         var ts = new Roo.TabPanel(
34100                 this.bodyEl.dom,
34101                 {
34102                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
34103                     disableTooltips: this.config.disableTabTips,
34104                     toolbar : this.config.toolbar
34105                 }
34106         );
34107         if(this.config.hideTabs){
34108             ts.stripWrap.setDisplayed(false);
34109         }
34110         this.tabs = ts;
34111         ts.resizeTabs = this.config.resizeTabs === true;
34112         ts.minTabWidth = this.config.minTabWidth || 40;
34113         ts.maxTabWidth = this.config.maxTabWidth || 250;
34114         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
34115         ts.monitorResize = false;
34116         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34117         ts.bodyEl.addClass('x-layout-tabs-body');
34118         this.panels.each(this.initPanelAsTab, this);
34119     },
34120
34121     initPanelAsTab : function(panel){
34122         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
34123                     this.config.closeOnTab && panel.isClosable());
34124         if(panel.tabTip !== undefined){
34125             ti.setTooltip(panel.tabTip);
34126         }
34127         ti.on("activate", function(){
34128               this.setActivePanel(panel);
34129         }, this);
34130         if(this.config.closeOnTab){
34131             ti.on("beforeclose", function(t, e){
34132                 e.cancel = true;
34133                 this.remove(panel);
34134             }, this);
34135         }
34136         return ti;
34137     },
34138
34139     updatePanelTitle : function(panel, title){
34140         if(this.activePanel == panel){
34141             this.updateTitle(title);
34142         }
34143         if(this.tabs){
34144             var ti = this.tabs.getTab(panel.getEl().id);
34145             ti.setText(title);
34146             if(panel.tabTip !== undefined){
34147                 ti.setTooltip(panel.tabTip);
34148             }
34149         }
34150     },
34151
34152     updateTitle : function(title){
34153         if(this.titleTextEl && !this.config.title){
34154             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
34155         }
34156     },
34157
34158     setActivePanel : function(panel){
34159         panel = this.getPanel(panel);
34160         if(this.activePanel && this.activePanel != panel){
34161             this.activePanel.setActiveState(false);
34162         }
34163         this.activePanel = panel;
34164         panel.setActiveState(true);
34165         if(this.panelSize){
34166             panel.setSize(this.panelSize.width, this.panelSize.height);
34167         }
34168         if(this.closeBtn){
34169             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
34170         }
34171         this.updateTitle(panel.getTitle());
34172         if(this.tabs){
34173             this.fireEvent("invalidated", this);
34174         }
34175         this.fireEvent("panelactivated", this, panel);
34176     },
34177
34178     /**
34179      * Shows the specified panel.
34180      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
34181      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
34182      */
34183     showPanel : function(panel)
34184     {
34185         panel = this.getPanel(panel);
34186         if(panel){
34187             if(this.tabs){
34188                 var tab = this.tabs.getTab(panel.getEl().id);
34189                 if(tab.isHidden()){
34190                     this.tabs.unhideTab(tab.id);
34191                 }
34192                 tab.activate();
34193             }else{
34194                 this.setActivePanel(panel);
34195             }
34196         }
34197         return panel;
34198     },
34199
34200     /**
34201      * Get the active panel for this region.
34202      * @return {Roo.ContentPanel} The active panel or null
34203      */
34204     getActivePanel : function(){
34205         return this.activePanel;
34206     },
34207
34208     validateVisibility : function(){
34209         if(this.panels.getCount() < 1){
34210             this.updateTitle("&#160;");
34211             this.closeBtn.hide();
34212             this.hide();
34213         }else{
34214             if(!this.isVisible()){
34215                 this.show();
34216             }
34217         }
34218     },
34219
34220     /**
34221      * Adds the passed ContentPanel(s) to this region.
34222      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34223      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
34224      */
34225     add : function(panel){
34226         if(arguments.length > 1){
34227             for(var i = 0, len = arguments.length; i < len; i++) {
34228                 this.add(arguments[i]);
34229             }
34230             return null;
34231         }
34232         if(this.hasPanel(panel)){
34233             this.showPanel(panel);
34234             return panel;
34235         }
34236         panel.setRegion(this);
34237         this.panels.add(panel);
34238         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
34239             this.bodyEl.dom.appendChild(panel.getEl().dom);
34240             if(panel.background !== true){
34241                 this.setActivePanel(panel);
34242             }
34243             this.fireEvent("paneladded", this, panel);
34244             return panel;
34245         }
34246         if(!this.tabs){
34247             this.initTabs();
34248         }else{
34249             this.initPanelAsTab(panel);
34250         }
34251         if(panel.background !== true){
34252             this.tabs.activate(panel.getEl().id);
34253         }
34254         this.fireEvent("paneladded", this, panel);
34255         return panel;
34256     },
34257
34258     /**
34259      * Hides the tab for the specified panel.
34260      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34261      */
34262     hidePanel : function(panel){
34263         if(this.tabs && (panel = this.getPanel(panel))){
34264             this.tabs.hideTab(panel.getEl().id);
34265         }
34266     },
34267
34268     /**
34269      * Unhides the tab for a previously hidden panel.
34270      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34271      */
34272     unhidePanel : function(panel){
34273         if(this.tabs && (panel = this.getPanel(panel))){
34274             this.tabs.unhideTab(panel.getEl().id);
34275         }
34276     },
34277
34278     clearPanels : function(){
34279         while(this.panels.getCount() > 0){
34280              this.remove(this.panels.first());
34281         }
34282     },
34283
34284     /**
34285      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34286      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34287      * @param {Boolean} preservePanel Overrides the config preservePanel option
34288      * @return {Roo.ContentPanel} The panel that was removed
34289      */
34290     remove : function(panel, preservePanel){
34291         panel = this.getPanel(panel);
34292         if(!panel){
34293             return null;
34294         }
34295         var e = {};
34296         this.fireEvent("beforeremove", this, panel, e);
34297         if(e.cancel === true){
34298             return null;
34299         }
34300         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
34301         var panelId = panel.getId();
34302         this.panels.removeKey(panelId);
34303         if(preservePanel){
34304             document.body.appendChild(panel.getEl().dom);
34305         }
34306         if(this.tabs){
34307             this.tabs.removeTab(panel.getEl().id);
34308         }else if (!preservePanel){
34309             this.bodyEl.dom.removeChild(panel.getEl().dom);
34310         }
34311         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
34312             var p = this.panels.first();
34313             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
34314             tempEl.appendChild(p.getEl().dom);
34315             this.bodyEl.update("");
34316             this.bodyEl.dom.appendChild(p.getEl().dom);
34317             tempEl = null;
34318             this.updateTitle(p.getTitle());
34319             this.tabs = null;
34320             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34321             this.setActivePanel(p);
34322         }
34323         panel.setRegion(null);
34324         if(this.activePanel == panel){
34325             this.activePanel = null;
34326         }
34327         if(this.config.autoDestroy !== false && preservePanel !== true){
34328             try{panel.destroy();}catch(e){}
34329         }
34330         this.fireEvent("panelremoved", this, panel);
34331         return panel;
34332     },
34333
34334     /**
34335      * Returns the TabPanel component used by this region
34336      * @return {Roo.TabPanel}
34337      */
34338     getTabs : function(){
34339         return this.tabs;
34340     },
34341
34342     createTool : function(parentEl, className){
34343         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
34344             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
34345         btn.addClassOnOver("x-layout-tools-button-over");
34346         return btn;
34347     }
34348 });/*
34349  * Based on:
34350  * Ext JS Library 1.1.1
34351  * Copyright(c) 2006-2007, Ext JS, LLC.
34352  *
34353  * Originally Released Under LGPL - original licence link has changed is not relivant.
34354  *
34355  * Fork - LGPL
34356  * <script type="text/javascript">
34357  */
34358  
34359
34360
34361 /**
34362  * @class Roo.SplitLayoutRegion
34363  * @extends Roo.LayoutRegion
34364  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
34365  */
34366 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
34367     this.cursor = cursor;
34368     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
34369 };
34370
34371 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
34372     splitTip : "Drag to resize.",
34373     collapsibleSplitTip : "Drag to resize. Double click to hide.",
34374     useSplitTips : false,
34375
34376     applyConfig : function(config){
34377         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
34378         if(config.split){
34379             if(!this.split){
34380                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
34381                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
34382                 /** The SplitBar for this region 
34383                 * @type Roo.SplitBar */
34384                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
34385                 this.split.on("moved", this.onSplitMove, this);
34386                 this.split.useShim = config.useShim === true;
34387                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
34388                 if(this.useSplitTips){
34389                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
34390                 }
34391                 if(config.collapsible){
34392                     this.split.el.on("dblclick", this.collapse,  this);
34393                 }
34394             }
34395             if(typeof config.minSize != "undefined"){
34396                 this.split.minSize = config.minSize;
34397             }
34398             if(typeof config.maxSize != "undefined"){
34399                 this.split.maxSize = config.maxSize;
34400             }
34401             if(config.hideWhenEmpty || config.hidden || config.collapsed){
34402                 this.hideSplitter();
34403             }
34404         }
34405     },
34406
34407     getHMaxSize : function(){
34408          var cmax = this.config.maxSize || 10000;
34409          var center = this.mgr.getRegion("center");
34410          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
34411     },
34412
34413     getVMaxSize : function(){
34414          var cmax = this.config.maxSize || 10000;
34415          var center = this.mgr.getRegion("center");
34416          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
34417     },
34418
34419     onSplitMove : function(split, newSize){
34420         this.fireEvent("resized", this, newSize);
34421     },
34422     
34423     /** 
34424      * Returns the {@link Roo.SplitBar} for this region.
34425      * @return {Roo.SplitBar}
34426      */
34427     getSplitBar : function(){
34428         return this.split;
34429     },
34430     
34431     hide : function(){
34432         this.hideSplitter();
34433         Roo.SplitLayoutRegion.superclass.hide.call(this);
34434     },
34435
34436     hideSplitter : function(){
34437         if(this.split){
34438             this.split.el.setLocation(-2000,-2000);
34439             this.split.el.hide();
34440         }
34441     },
34442
34443     show : function(){
34444         if(this.split){
34445             this.split.el.show();
34446         }
34447         Roo.SplitLayoutRegion.superclass.show.call(this);
34448     },
34449     
34450     beforeSlide: function(){
34451         if(Roo.isGecko){// firefox overflow auto bug workaround
34452             this.bodyEl.clip();
34453             if(this.tabs) this.tabs.bodyEl.clip();
34454             if(this.activePanel){
34455                 this.activePanel.getEl().clip();
34456                 
34457                 if(this.activePanel.beforeSlide){
34458                     this.activePanel.beforeSlide();
34459                 }
34460             }
34461         }
34462     },
34463     
34464     afterSlide : function(){
34465         if(Roo.isGecko){// firefox overflow auto bug workaround
34466             this.bodyEl.unclip();
34467             if(this.tabs) this.tabs.bodyEl.unclip();
34468             if(this.activePanel){
34469                 this.activePanel.getEl().unclip();
34470                 if(this.activePanel.afterSlide){
34471                     this.activePanel.afterSlide();
34472                 }
34473             }
34474         }
34475     },
34476
34477     initAutoHide : function(){
34478         if(this.autoHide !== false){
34479             if(!this.autoHideHd){
34480                 var st = new Roo.util.DelayedTask(this.slideIn, this);
34481                 this.autoHideHd = {
34482                     "mouseout": function(e){
34483                         if(!e.within(this.el, true)){
34484                             st.delay(500);
34485                         }
34486                     },
34487                     "mouseover" : function(e){
34488                         st.cancel();
34489                     },
34490                     scope : this
34491                 };
34492             }
34493             this.el.on(this.autoHideHd);
34494         }
34495     },
34496
34497     clearAutoHide : function(){
34498         if(this.autoHide !== false){
34499             this.el.un("mouseout", this.autoHideHd.mouseout);
34500             this.el.un("mouseover", this.autoHideHd.mouseover);
34501         }
34502     },
34503
34504     clearMonitor : function(){
34505         Roo.get(document).un("click", this.slideInIf, this);
34506     },
34507
34508     // these names are backwards but not changed for compat
34509     slideOut : function(){
34510         if(this.isSlid || this.el.hasActiveFx()){
34511             return;
34512         }
34513         this.isSlid = true;
34514         if(this.collapseBtn){
34515             this.collapseBtn.hide();
34516         }
34517         this.closeBtnState = this.closeBtn.getStyle('display');
34518         this.closeBtn.hide();
34519         if(this.stickBtn){
34520             this.stickBtn.show();
34521         }
34522         this.el.show();
34523         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34524         this.beforeSlide();
34525         this.el.setStyle("z-index", 10001);
34526         this.el.slideIn(this.getSlideAnchor(), {
34527             callback: function(){
34528                 this.afterSlide();
34529                 this.initAutoHide();
34530                 Roo.get(document).on("click", this.slideInIf, this);
34531                 this.fireEvent("slideshow", this);
34532             },
34533             scope: this,
34534             block: true
34535         });
34536     },
34537
34538     afterSlideIn : function(){
34539         this.clearAutoHide();
34540         this.isSlid = false;
34541         this.clearMonitor();
34542         this.el.setStyle("z-index", "");
34543         if(this.collapseBtn){
34544             this.collapseBtn.show();
34545         }
34546         this.closeBtn.setStyle('display', this.closeBtnState);
34547         if(this.stickBtn){
34548             this.stickBtn.hide();
34549         }
34550         this.fireEvent("slidehide", this);
34551     },
34552
34553     slideIn : function(cb){
34554         if(!this.isSlid || this.el.hasActiveFx()){
34555             Roo.callback(cb);
34556             return;
34557         }
34558         this.isSlid = false;
34559         this.beforeSlide();
34560         this.el.slideOut(this.getSlideAnchor(), {
34561             callback: function(){
34562                 this.el.setLeftTop(-10000, -10000);
34563                 this.afterSlide();
34564                 this.afterSlideIn();
34565                 Roo.callback(cb);
34566             },
34567             scope: this,
34568             block: true
34569         });
34570     },
34571     
34572     slideInIf : function(e){
34573         if(!e.within(this.el)){
34574             this.slideIn();
34575         }
34576     },
34577
34578     animateCollapse : function(){
34579         this.beforeSlide();
34580         this.el.setStyle("z-index", 20000);
34581         var anchor = this.getSlideAnchor();
34582         this.el.slideOut(anchor, {
34583             callback : function(){
34584                 this.el.setStyle("z-index", "");
34585                 this.collapsedEl.slideIn(anchor, {duration:.3});
34586                 this.afterSlide();
34587                 this.el.setLocation(-10000,-10000);
34588                 this.el.hide();
34589                 this.fireEvent("collapsed", this);
34590             },
34591             scope: this,
34592             block: true
34593         });
34594     },
34595
34596     animateExpand : function(){
34597         this.beforeSlide();
34598         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34599         this.el.setStyle("z-index", 20000);
34600         this.collapsedEl.hide({
34601             duration:.1
34602         });
34603         this.el.slideIn(this.getSlideAnchor(), {
34604             callback : function(){
34605                 this.el.setStyle("z-index", "");
34606                 this.afterSlide();
34607                 if(this.split){
34608                     this.split.el.show();
34609                 }
34610                 this.fireEvent("invalidated", this);
34611                 this.fireEvent("expanded", this);
34612             },
34613             scope: this,
34614             block: true
34615         });
34616     },
34617
34618     anchors : {
34619         "west" : "left",
34620         "east" : "right",
34621         "north" : "top",
34622         "south" : "bottom"
34623     },
34624
34625     sanchors : {
34626         "west" : "l",
34627         "east" : "r",
34628         "north" : "t",
34629         "south" : "b"
34630     },
34631
34632     canchors : {
34633         "west" : "tl-tr",
34634         "east" : "tr-tl",
34635         "north" : "tl-bl",
34636         "south" : "bl-tl"
34637     },
34638
34639     getAnchor : function(){
34640         return this.anchors[this.position];
34641     },
34642
34643     getCollapseAnchor : function(){
34644         return this.canchors[this.position];
34645     },
34646
34647     getSlideAnchor : function(){
34648         return this.sanchors[this.position];
34649     },
34650
34651     getAlignAdj : function(){
34652         var cm = this.cmargins;
34653         switch(this.position){
34654             case "west":
34655                 return [0, 0];
34656             break;
34657             case "east":
34658                 return [0, 0];
34659             break;
34660             case "north":
34661                 return [0, 0];
34662             break;
34663             case "south":
34664                 return [0, 0];
34665             break;
34666         }
34667     },
34668
34669     getExpandAdj : function(){
34670         var c = this.collapsedEl, cm = this.cmargins;
34671         switch(this.position){
34672             case "west":
34673                 return [-(cm.right+c.getWidth()+cm.left), 0];
34674             break;
34675             case "east":
34676                 return [cm.right+c.getWidth()+cm.left, 0];
34677             break;
34678             case "north":
34679                 return [0, -(cm.top+cm.bottom+c.getHeight())];
34680             break;
34681             case "south":
34682                 return [0, cm.top+cm.bottom+c.getHeight()];
34683             break;
34684         }
34685     }
34686 });/*
34687  * Based on:
34688  * Ext JS Library 1.1.1
34689  * Copyright(c) 2006-2007, Ext JS, LLC.
34690  *
34691  * Originally Released Under LGPL - original licence link has changed is not relivant.
34692  *
34693  * Fork - LGPL
34694  * <script type="text/javascript">
34695  */
34696 /*
34697  * These classes are private internal classes
34698  */
34699 Roo.CenterLayoutRegion = function(mgr, config){
34700     Roo.LayoutRegion.call(this, mgr, config, "center");
34701     this.visible = true;
34702     this.minWidth = config.minWidth || 20;
34703     this.minHeight = config.minHeight || 20;
34704 };
34705
34706 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
34707     hide : function(){
34708         // center panel can't be hidden
34709     },
34710     
34711     show : function(){
34712         // center panel can't be hidden
34713     },
34714     
34715     getMinWidth: function(){
34716         return this.minWidth;
34717     },
34718     
34719     getMinHeight: function(){
34720         return this.minHeight;
34721     }
34722 });
34723
34724
34725 Roo.NorthLayoutRegion = function(mgr, config){
34726     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
34727     if(this.split){
34728         this.split.placement = Roo.SplitBar.TOP;
34729         this.split.orientation = Roo.SplitBar.VERTICAL;
34730         this.split.el.addClass("x-layout-split-v");
34731     }
34732     var size = config.initialSize || config.height;
34733     if(typeof size != "undefined"){
34734         this.el.setHeight(size);
34735     }
34736 };
34737 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
34738     orientation: Roo.SplitBar.VERTICAL,
34739     getBox : function(){
34740         if(this.collapsed){
34741             return this.collapsedEl.getBox();
34742         }
34743         var box = this.el.getBox();
34744         if(this.split){
34745             box.height += this.split.el.getHeight();
34746         }
34747         return box;
34748     },
34749     
34750     updateBox : function(box){
34751         if(this.split && !this.collapsed){
34752             box.height -= this.split.el.getHeight();
34753             this.split.el.setLeft(box.x);
34754             this.split.el.setTop(box.y+box.height);
34755             this.split.el.setWidth(box.width);
34756         }
34757         if(this.collapsed){
34758             this.updateBody(box.width, null);
34759         }
34760         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34761     }
34762 });
34763
34764 Roo.SouthLayoutRegion = function(mgr, config){
34765     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
34766     if(this.split){
34767         this.split.placement = Roo.SplitBar.BOTTOM;
34768         this.split.orientation = Roo.SplitBar.VERTICAL;
34769         this.split.el.addClass("x-layout-split-v");
34770     }
34771     var size = config.initialSize || config.height;
34772     if(typeof size != "undefined"){
34773         this.el.setHeight(size);
34774     }
34775 };
34776 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
34777     orientation: Roo.SplitBar.VERTICAL,
34778     getBox : function(){
34779         if(this.collapsed){
34780             return this.collapsedEl.getBox();
34781         }
34782         var box = this.el.getBox();
34783         if(this.split){
34784             var sh = this.split.el.getHeight();
34785             box.height += sh;
34786             box.y -= sh;
34787         }
34788         return box;
34789     },
34790     
34791     updateBox : function(box){
34792         if(this.split && !this.collapsed){
34793             var sh = this.split.el.getHeight();
34794             box.height -= sh;
34795             box.y += sh;
34796             this.split.el.setLeft(box.x);
34797             this.split.el.setTop(box.y-sh);
34798             this.split.el.setWidth(box.width);
34799         }
34800         if(this.collapsed){
34801             this.updateBody(box.width, null);
34802         }
34803         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34804     }
34805 });
34806
34807 Roo.EastLayoutRegion = function(mgr, config){
34808     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
34809     if(this.split){
34810         this.split.placement = Roo.SplitBar.RIGHT;
34811         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34812         this.split.el.addClass("x-layout-split-h");
34813     }
34814     var size = config.initialSize || config.width;
34815     if(typeof size != "undefined"){
34816         this.el.setWidth(size);
34817     }
34818 };
34819 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
34820     orientation: Roo.SplitBar.HORIZONTAL,
34821     getBox : function(){
34822         if(this.collapsed){
34823             return this.collapsedEl.getBox();
34824         }
34825         var box = this.el.getBox();
34826         if(this.split){
34827             var sw = this.split.el.getWidth();
34828             box.width += sw;
34829             box.x -= sw;
34830         }
34831         return box;
34832     },
34833
34834     updateBox : function(box){
34835         if(this.split && !this.collapsed){
34836             var sw = this.split.el.getWidth();
34837             box.width -= sw;
34838             this.split.el.setLeft(box.x);
34839             this.split.el.setTop(box.y);
34840             this.split.el.setHeight(box.height);
34841             box.x += sw;
34842         }
34843         if(this.collapsed){
34844             this.updateBody(null, box.height);
34845         }
34846         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34847     }
34848 });
34849
34850 Roo.WestLayoutRegion = function(mgr, config){
34851     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
34852     if(this.split){
34853         this.split.placement = Roo.SplitBar.LEFT;
34854         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34855         this.split.el.addClass("x-layout-split-h");
34856     }
34857     var size = config.initialSize || config.width;
34858     if(typeof size != "undefined"){
34859         this.el.setWidth(size);
34860     }
34861 };
34862 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
34863     orientation: Roo.SplitBar.HORIZONTAL,
34864     getBox : function(){
34865         if(this.collapsed){
34866             return this.collapsedEl.getBox();
34867         }
34868         var box = this.el.getBox();
34869         if(this.split){
34870             box.width += this.split.el.getWidth();
34871         }
34872         return box;
34873     },
34874     
34875     updateBox : function(box){
34876         if(this.split && !this.collapsed){
34877             var sw = this.split.el.getWidth();
34878             box.width -= sw;
34879             this.split.el.setLeft(box.x+box.width);
34880             this.split.el.setTop(box.y);
34881             this.split.el.setHeight(box.height);
34882         }
34883         if(this.collapsed){
34884             this.updateBody(null, box.height);
34885         }
34886         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34887     }
34888 });
34889 /*
34890  * Based on:
34891  * Ext JS Library 1.1.1
34892  * Copyright(c) 2006-2007, Ext JS, LLC.
34893  *
34894  * Originally Released Under LGPL - original licence link has changed is not relivant.
34895  *
34896  * Fork - LGPL
34897  * <script type="text/javascript">
34898  */
34899  
34900  
34901 /*
34902  * Private internal class for reading and applying state
34903  */
34904 Roo.LayoutStateManager = function(layout){
34905      // default empty state
34906      this.state = {
34907         north: {},
34908         south: {},
34909         east: {},
34910         west: {}       
34911     };
34912 };
34913
34914 Roo.LayoutStateManager.prototype = {
34915     init : function(layout, provider){
34916         this.provider = provider;
34917         var state = provider.get(layout.id+"-layout-state");
34918         if(state){
34919             var wasUpdating = layout.isUpdating();
34920             if(!wasUpdating){
34921                 layout.beginUpdate();
34922             }
34923             for(var key in state){
34924                 if(typeof state[key] != "function"){
34925                     var rstate = state[key];
34926                     var r = layout.getRegion(key);
34927                     if(r && rstate){
34928                         if(rstate.size){
34929                             r.resizeTo(rstate.size);
34930                         }
34931                         if(rstate.collapsed == true){
34932                             r.collapse(true);
34933                         }else{
34934                             r.expand(null, true);
34935                         }
34936                     }
34937                 }
34938             }
34939             if(!wasUpdating){
34940                 layout.endUpdate();
34941             }
34942             this.state = state; 
34943         }
34944         this.layout = layout;
34945         layout.on("regionresized", this.onRegionResized, this);
34946         layout.on("regioncollapsed", this.onRegionCollapsed, this);
34947         layout.on("regionexpanded", this.onRegionExpanded, this);
34948     },
34949     
34950     storeState : function(){
34951         this.provider.set(this.layout.id+"-layout-state", this.state);
34952     },
34953     
34954     onRegionResized : function(region, newSize){
34955         this.state[region.getPosition()].size = newSize;
34956         this.storeState();
34957     },
34958     
34959     onRegionCollapsed : function(region){
34960         this.state[region.getPosition()].collapsed = true;
34961         this.storeState();
34962     },
34963     
34964     onRegionExpanded : function(region){
34965         this.state[region.getPosition()].collapsed = false;
34966         this.storeState();
34967     }
34968 };/*
34969  * Based on:
34970  * Ext JS Library 1.1.1
34971  * Copyright(c) 2006-2007, Ext JS, LLC.
34972  *
34973  * Originally Released Under LGPL - original licence link has changed is not relivant.
34974  *
34975  * Fork - LGPL
34976  * <script type="text/javascript">
34977  */
34978 /**
34979  * @class Roo.ContentPanel
34980  * @extends Roo.util.Observable
34981  * A basic ContentPanel element.
34982  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
34983  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
34984  * @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
34985  * @cfg {Boolean}   closable      True if the panel can be closed/removed
34986  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
34987  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34988  * @cfg {Toolbar}   toolbar       A toolbar for this panel
34989  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
34990  * @cfg {String} title          The title for this panel
34991  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34992  * @cfg {String} url            Calls {@link #setUrl} with this value
34993  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34994  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
34995  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
34996  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
34997
34998  * @constructor
34999  * Create a new ContentPanel.
35000  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
35001  * @param {String/Object} config A string to set only the title or a config object
35002  * @param {String} content (optional) Set the HTML content for this panel
35003  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
35004  */
35005 Roo.ContentPanel = function(el, config, content){
35006     
35007      
35008     /*
35009     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
35010         config = el;
35011         el = Roo.id();
35012     }
35013     if (config && config.parentLayout) { 
35014         el = config.parentLayout.el.createChild(); 
35015     }
35016     */
35017     if(el.autoCreate){ // xtype is available if this is called from factory
35018         config = el;
35019         el = Roo.id();
35020     }
35021     this.el = Roo.get(el);
35022     if(!this.el && config && config.autoCreate){
35023         if(typeof config.autoCreate == "object"){
35024             if(!config.autoCreate.id){
35025                 config.autoCreate.id = config.id||el;
35026             }
35027             this.el = Roo.DomHelper.append(document.body,
35028                         config.autoCreate, true);
35029         }else{
35030             this.el = Roo.DomHelper.append(document.body,
35031                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
35032         }
35033     }
35034     this.closable = false;
35035     this.loaded = false;
35036     this.active = false;
35037     if(typeof config == "string"){
35038         this.title = config;
35039     }else{
35040         Roo.apply(this, config);
35041     }
35042     
35043     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
35044         this.wrapEl = this.el.wrap();
35045         this.toolbar.container = this.el.insertSibling(false, 'before');
35046         this.toolbar = new Roo.Toolbar(this.toolbar);
35047     }
35048     
35049     // xtype created footer. - not sure if will work as we normally have to render first..
35050     if (this.footer && !this.footer.el && this.footer.xtype) {
35051         if (!this.wrapEl) {
35052             this.wrapEl = this.el.wrap();
35053         }
35054     
35055         this.footer.container = this.wrapEl.createChild();
35056          
35057         this.footer = Roo.factory(this.footer, Roo);
35058         
35059     }
35060     
35061     if(this.resizeEl){
35062         this.resizeEl = Roo.get(this.resizeEl, true);
35063     }else{
35064         this.resizeEl = this.el;
35065     }
35066     // handle view.xtype
35067     
35068  
35069     
35070     
35071     this.addEvents({
35072         /**
35073          * @event activate
35074          * Fires when this panel is activated. 
35075          * @param {Roo.ContentPanel} this
35076          */
35077         "activate" : true,
35078         /**
35079          * @event deactivate
35080          * Fires when this panel is activated. 
35081          * @param {Roo.ContentPanel} this
35082          */
35083         "deactivate" : true,
35084
35085         /**
35086          * @event resize
35087          * Fires when this panel is resized if fitToFrame is true.
35088          * @param {Roo.ContentPanel} this
35089          * @param {Number} width The width after any component adjustments
35090          * @param {Number} height The height after any component adjustments
35091          */
35092         "resize" : true,
35093         
35094          /**
35095          * @event render
35096          * Fires when this tab is created
35097          * @param {Roo.ContentPanel} this
35098          */
35099         "render" : true
35100         
35101         
35102         
35103     });
35104     
35105
35106     
35107     
35108     if(this.autoScroll){
35109         this.resizeEl.setStyle("overflow", "auto");
35110     } else {
35111         // fix randome scrolling
35112         this.el.on('scroll', function() {
35113             Roo.log('fix random scolling');
35114             this.scrollTo('top',0); 
35115         });
35116     }
35117     content = content || this.content;
35118     if(content){
35119         this.setContent(content);
35120     }
35121     if(config && config.url){
35122         this.setUrl(this.url, this.params, this.loadOnce);
35123     }
35124     
35125     
35126     
35127     Roo.ContentPanel.superclass.constructor.call(this);
35128     
35129     if (this.view && typeof(this.view.xtype) != 'undefined') {
35130         this.view.el = this.el.appendChild(document.createElement("div"));
35131         this.view = Roo.factory(this.view); 
35132         this.view.render  &&  this.view.render(false, '');  
35133     }
35134     
35135     
35136     this.fireEvent('render', this);
35137 };
35138
35139 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
35140     tabTip:'',
35141     setRegion : function(region){
35142         this.region = region;
35143         if(region){
35144            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
35145         }else{
35146            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
35147         } 
35148     },
35149     
35150     /**
35151      * Returns the toolbar for this Panel if one was configured. 
35152      * @return {Roo.Toolbar} 
35153      */
35154     getToolbar : function(){
35155         return this.toolbar;
35156     },
35157     
35158     setActiveState : function(active){
35159         this.active = active;
35160         if(!active){
35161             this.fireEvent("deactivate", this);
35162         }else{
35163             this.fireEvent("activate", this);
35164         }
35165     },
35166     /**
35167      * Updates this panel's element
35168      * @param {String} content The new content
35169      * @param {Boolean} loadScripts (optional) true to look for and process scripts
35170     */
35171     setContent : function(content, loadScripts){
35172         this.el.update(content, loadScripts);
35173     },
35174
35175     ignoreResize : function(w, h){
35176         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
35177             return true;
35178         }else{
35179             this.lastSize = {width: w, height: h};
35180             return false;
35181         }
35182     },
35183     /**
35184      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
35185      * @return {Roo.UpdateManager} The UpdateManager
35186      */
35187     getUpdateManager : function(){
35188         return this.el.getUpdateManager();
35189     },
35190      /**
35191      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
35192      * @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:
35193 <pre><code>
35194 panel.load({
35195     url: "your-url.php",
35196     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
35197     callback: yourFunction,
35198     scope: yourObject, //(optional scope)
35199     discardUrl: false,
35200     nocache: false,
35201     text: "Loading...",
35202     timeout: 30,
35203     scripts: false
35204 });
35205 </code></pre>
35206      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
35207      * 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.
35208      * @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}
35209      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
35210      * @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.
35211      * @return {Roo.ContentPanel} this
35212      */
35213     load : function(){
35214         var um = this.el.getUpdateManager();
35215         um.update.apply(um, arguments);
35216         return this;
35217     },
35218
35219
35220     /**
35221      * 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.
35222      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
35223      * @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)
35224      * @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)
35225      * @return {Roo.UpdateManager} The UpdateManager
35226      */
35227     setUrl : function(url, params, loadOnce){
35228         if(this.refreshDelegate){
35229             this.removeListener("activate", this.refreshDelegate);
35230         }
35231         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35232         this.on("activate", this.refreshDelegate);
35233         return this.el.getUpdateManager();
35234     },
35235     
35236     _handleRefresh : function(url, params, loadOnce){
35237         if(!loadOnce || !this.loaded){
35238             var updater = this.el.getUpdateManager();
35239             updater.update(url, params, this._setLoaded.createDelegate(this));
35240         }
35241     },
35242     
35243     _setLoaded : function(){
35244         this.loaded = true;
35245     }, 
35246     
35247     /**
35248      * Returns this panel's id
35249      * @return {String} 
35250      */
35251     getId : function(){
35252         return this.el.id;
35253     },
35254     
35255     /** 
35256      * Returns this panel's element - used by regiosn to add.
35257      * @return {Roo.Element} 
35258      */
35259     getEl : function(){
35260         return this.wrapEl || this.el;
35261     },
35262     
35263     adjustForComponents : function(width, height)
35264     {
35265         //Roo.log('adjustForComponents ');
35266         if(this.resizeEl != this.el){
35267             width -= this.el.getFrameWidth('lr');
35268             height -= this.el.getFrameWidth('tb');
35269         }
35270         if(this.toolbar){
35271             var te = this.toolbar.getEl();
35272             height -= te.getHeight();
35273             te.setWidth(width);
35274         }
35275         if(this.footer){
35276             var te = this.footer.getEl();
35277             Roo.log("footer:" + te.getHeight());
35278             
35279             height -= te.getHeight();
35280             te.setWidth(width);
35281         }
35282         
35283         
35284         if(this.adjustments){
35285             width += this.adjustments[0];
35286             height += this.adjustments[1];
35287         }
35288         return {"width": width, "height": height};
35289     },
35290     
35291     setSize : function(width, height){
35292         if(this.fitToFrame && !this.ignoreResize(width, height)){
35293             if(this.fitContainer && this.resizeEl != this.el){
35294                 this.el.setSize(width, height);
35295             }
35296             var size = this.adjustForComponents(width, height);
35297             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
35298             this.fireEvent('resize', this, size.width, size.height);
35299         }
35300     },
35301     
35302     /**
35303      * Returns this panel's title
35304      * @return {String} 
35305      */
35306     getTitle : function(){
35307         return this.title;
35308     },
35309     
35310     /**
35311      * Set this panel's title
35312      * @param {String} title
35313      */
35314     setTitle : function(title){
35315         this.title = title;
35316         if(this.region){
35317             this.region.updatePanelTitle(this, title);
35318         }
35319     },
35320     
35321     /**
35322      * Returns true is this panel was configured to be closable
35323      * @return {Boolean} 
35324      */
35325     isClosable : function(){
35326         return this.closable;
35327     },
35328     
35329     beforeSlide : function(){
35330         this.el.clip();
35331         this.resizeEl.clip();
35332     },
35333     
35334     afterSlide : function(){
35335         this.el.unclip();
35336         this.resizeEl.unclip();
35337     },
35338     
35339     /**
35340      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
35341      *   Will fail silently if the {@link #setUrl} method has not been called.
35342      *   This does not activate the panel, just updates its content.
35343      */
35344     refresh : function(){
35345         if(this.refreshDelegate){
35346            this.loaded = false;
35347            this.refreshDelegate();
35348         }
35349     },
35350     
35351     /**
35352      * Destroys this panel
35353      */
35354     destroy : function(){
35355         this.el.removeAllListeners();
35356         var tempEl = document.createElement("span");
35357         tempEl.appendChild(this.el.dom);
35358         tempEl.innerHTML = "";
35359         this.el.remove();
35360         this.el = null;
35361     },
35362     
35363     /**
35364      * form - if the content panel contains a form - this is a reference to it.
35365      * @type {Roo.form.Form}
35366      */
35367     form : false,
35368     /**
35369      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
35370      *    This contains a reference to it.
35371      * @type {Roo.View}
35372      */
35373     view : false,
35374     
35375       /**
35376      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
35377      * <pre><code>
35378
35379 layout.addxtype({
35380        xtype : 'Form',
35381        items: [ .... ]
35382    }
35383 );
35384
35385 </code></pre>
35386      * @param {Object} cfg Xtype definition of item to add.
35387      */
35388     
35389     addxtype : function(cfg) {
35390         // add form..
35391         if (cfg.xtype.match(/^Form$/)) {
35392             
35393             var el;
35394             //if (this.footer) {
35395             //    el = this.footer.container.insertSibling(false, 'before');
35396             //} else {
35397                 el = this.el.createChild();
35398             //}
35399
35400             this.form = new  Roo.form.Form(cfg);
35401             
35402             
35403             if ( this.form.allItems.length) this.form.render(el.dom);
35404             return this.form;
35405         }
35406         // should only have one of theses..
35407         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
35408             // views.. should not be just added - used named prop 'view''
35409             
35410             cfg.el = this.el.appendChild(document.createElement("div"));
35411             // factory?
35412             
35413             var ret = new Roo.factory(cfg);
35414              
35415              ret.render && ret.render(false, ''); // render blank..
35416             this.view = ret;
35417             return ret;
35418         }
35419         return false;
35420     }
35421 });
35422
35423 /**
35424  * @class Roo.GridPanel
35425  * @extends Roo.ContentPanel
35426  * @constructor
35427  * Create a new GridPanel.
35428  * @param {Roo.grid.Grid} grid The grid for this panel
35429  * @param {String/Object} config A string to set only the panel's title, or a config object
35430  */
35431 Roo.GridPanel = function(grid, config){
35432     
35433   
35434     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
35435         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
35436         
35437     this.wrapper.dom.appendChild(grid.getGridEl().dom);
35438     
35439     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
35440     
35441     if(this.toolbar){
35442         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
35443     }
35444     // xtype created footer. - not sure if will work as we normally have to render first..
35445     if (this.footer && !this.footer.el && this.footer.xtype) {
35446         
35447         this.footer.container = this.grid.getView().getFooterPanel(true);
35448         this.footer.dataSource = this.grid.dataSource;
35449         this.footer = Roo.factory(this.footer, Roo);
35450         
35451     }
35452     
35453     grid.monitorWindowResize = false; // turn off autosizing
35454     grid.autoHeight = false;
35455     grid.autoWidth = false;
35456     this.grid = grid;
35457     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
35458 };
35459
35460 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
35461     getId : function(){
35462         return this.grid.id;
35463     },
35464     
35465     /**
35466      * Returns the grid for this panel
35467      * @return {Roo.grid.Grid} 
35468      */
35469     getGrid : function(){
35470         return this.grid;    
35471     },
35472     
35473     setSize : function(width, height){
35474         if(!this.ignoreResize(width, height)){
35475             var grid = this.grid;
35476             var size = this.adjustForComponents(width, height);
35477             grid.getGridEl().setSize(size.width, size.height);
35478             grid.autoSize();
35479         }
35480     },
35481     
35482     beforeSlide : function(){
35483         this.grid.getView().scroller.clip();
35484     },
35485     
35486     afterSlide : function(){
35487         this.grid.getView().scroller.unclip();
35488     },
35489     
35490     destroy : function(){
35491         this.grid.destroy();
35492         delete this.grid;
35493         Roo.GridPanel.superclass.destroy.call(this); 
35494     }
35495 });
35496
35497
35498 /**
35499  * @class Roo.NestedLayoutPanel
35500  * @extends Roo.ContentPanel
35501  * @constructor
35502  * Create a new NestedLayoutPanel.
35503  * 
35504  * 
35505  * @param {Roo.BorderLayout} layout The layout for this panel
35506  * @param {String/Object} config A string to set only the title or a config object
35507  */
35508 Roo.NestedLayoutPanel = function(layout, config)
35509 {
35510     // construct with only one argument..
35511     /* FIXME - implement nicer consturctors
35512     if (layout.layout) {
35513         config = layout;
35514         layout = config.layout;
35515         delete config.layout;
35516     }
35517     if (layout.xtype && !layout.getEl) {
35518         // then layout needs constructing..
35519         layout = Roo.factory(layout, Roo);
35520     }
35521     */
35522     
35523     
35524     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
35525     
35526     layout.monitorWindowResize = false; // turn off autosizing
35527     this.layout = layout;
35528     this.layout.getEl().addClass("x-layout-nested-layout");
35529     
35530     
35531     
35532     
35533 };
35534
35535 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
35536
35537     setSize : function(width, height){
35538         if(!this.ignoreResize(width, height)){
35539             var size = this.adjustForComponents(width, height);
35540             var el = this.layout.getEl();
35541             el.setSize(size.width, size.height);
35542             var touch = el.dom.offsetWidth;
35543             this.layout.layout();
35544             // ie requires a double layout on the first pass
35545             if(Roo.isIE && !this.initialized){
35546                 this.initialized = true;
35547                 this.layout.layout();
35548             }
35549         }
35550     },
35551     
35552     // activate all subpanels if not currently active..
35553     
35554     setActiveState : function(active){
35555         this.active = active;
35556         if(!active){
35557             this.fireEvent("deactivate", this);
35558             return;
35559         }
35560         
35561         this.fireEvent("activate", this);
35562         // not sure if this should happen before or after..
35563         if (!this.layout) {
35564             return; // should not happen..
35565         }
35566         var reg = false;
35567         for (var r in this.layout.regions) {
35568             reg = this.layout.getRegion(r);
35569             if (reg.getActivePanel()) {
35570                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
35571                 reg.setActivePanel(reg.getActivePanel());
35572                 continue;
35573             }
35574             if (!reg.panels.length) {
35575                 continue;
35576             }
35577             reg.showPanel(reg.getPanel(0));
35578         }
35579         
35580         
35581         
35582         
35583     },
35584     
35585     /**
35586      * Returns the nested BorderLayout for this panel
35587      * @return {Roo.BorderLayout} 
35588      */
35589     getLayout : function(){
35590         return this.layout;
35591     },
35592     
35593      /**
35594      * Adds a xtype elements to the layout of the nested panel
35595      * <pre><code>
35596
35597 panel.addxtype({
35598        xtype : 'ContentPanel',
35599        region: 'west',
35600        items: [ .... ]
35601    }
35602 );
35603
35604 panel.addxtype({
35605         xtype : 'NestedLayoutPanel',
35606         region: 'west',
35607         layout: {
35608            center: { },
35609            west: { }   
35610         },
35611         items : [ ... list of content panels or nested layout panels.. ]
35612    }
35613 );
35614 </code></pre>
35615      * @param {Object} cfg Xtype definition of item to add.
35616      */
35617     addxtype : function(cfg) {
35618         return this.layout.addxtype(cfg);
35619     
35620     }
35621 });
35622
35623 Roo.ScrollPanel = function(el, config, content){
35624     config = config || {};
35625     config.fitToFrame = true;
35626     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
35627     
35628     this.el.dom.style.overflow = "hidden";
35629     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
35630     this.el.removeClass("x-layout-inactive-content");
35631     this.el.on("mousewheel", this.onWheel, this);
35632
35633     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
35634     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
35635     up.unselectable(); down.unselectable();
35636     up.on("click", this.scrollUp, this);
35637     down.on("click", this.scrollDown, this);
35638     up.addClassOnOver("x-scroller-btn-over");
35639     down.addClassOnOver("x-scroller-btn-over");
35640     up.addClassOnClick("x-scroller-btn-click");
35641     down.addClassOnClick("x-scroller-btn-click");
35642     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
35643
35644     this.resizeEl = this.el;
35645     this.el = wrap; this.up = up; this.down = down;
35646 };
35647
35648 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
35649     increment : 100,
35650     wheelIncrement : 5,
35651     scrollUp : function(){
35652         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
35653     },
35654
35655     scrollDown : function(){
35656         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
35657     },
35658
35659     afterScroll : function(){
35660         var el = this.resizeEl;
35661         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
35662         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35663         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35664     },
35665
35666     setSize : function(){
35667         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
35668         this.afterScroll();
35669     },
35670
35671     onWheel : function(e){
35672         var d = e.getWheelDelta();
35673         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
35674         this.afterScroll();
35675         e.stopEvent();
35676     },
35677
35678     setContent : function(content, loadScripts){
35679         this.resizeEl.update(content, loadScripts);
35680     }
35681
35682 });
35683
35684
35685
35686
35687
35688
35689
35690
35691
35692 /**
35693  * @class Roo.TreePanel
35694  * @extends Roo.ContentPanel
35695  * @constructor
35696  * Create a new TreePanel. - defaults to fit/scoll contents.
35697  * @param {String/Object} config A string to set only the panel's title, or a config object
35698  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
35699  */
35700 Roo.TreePanel = function(config){
35701     var el = config.el;
35702     var tree = config.tree;
35703     delete config.tree; 
35704     delete config.el; // hopefull!
35705     
35706     // wrapper for IE7 strict & safari scroll issue
35707     
35708     var treeEl = el.createChild();
35709     config.resizeEl = treeEl;
35710     
35711     
35712     
35713     Roo.TreePanel.superclass.constructor.call(this, el, config);
35714  
35715  
35716     this.tree = new Roo.tree.TreePanel(treeEl , tree);
35717     //console.log(tree);
35718     this.on('activate', function()
35719     {
35720         if (this.tree.rendered) {
35721             return;
35722         }
35723         //console.log('render tree');
35724         this.tree.render();
35725     });
35726     // this should not be needed.. - it's actually the 'el' that resizes?
35727     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
35728     
35729     //this.on('resize',  function (cp, w, h) {
35730     //        this.tree.innerCt.setWidth(w);
35731     //        this.tree.innerCt.setHeight(h);
35732     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
35733     //});
35734
35735         
35736     
35737 };
35738
35739 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
35740     fitToFrame : true,
35741     autoScroll : true
35742 });
35743
35744
35745
35746
35747
35748
35749
35750
35751
35752
35753
35754 /*
35755  * Based on:
35756  * Ext JS Library 1.1.1
35757  * Copyright(c) 2006-2007, Ext JS, LLC.
35758  *
35759  * Originally Released Under LGPL - original licence link has changed is not relivant.
35760  *
35761  * Fork - LGPL
35762  * <script type="text/javascript">
35763  */
35764  
35765
35766 /**
35767  * @class Roo.ReaderLayout
35768  * @extends Roo.BorderLayout
35769  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
35770  * center region containing two nested regions (a top one for a list view and one for item preview below),
35771  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
35772  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
35773  * expedites the setup of the overall layout and regions for this common application style.
35774  * Example:
35775  <pre><code>
35776 var reader = new Roo.ReaderLayout();
35777 var CP = Roo.ContentPanel;  // shortcut for adding
35778
35779 reader.beginUpdate();
35780 reader.add("north", new CP("north", "North"));
35781 reader.add("west", new CP("west", {title: "West"}));
35782 reader.add("east", new CP("east", {title: "East"}));
35783
35784 reader.regions.listView.add(new CP("listView", "List"));
35785 reader.regions.preview.add(new CP("preview", "Preview"));
35786 reader.endUpdate();
35787 </code></pre>
35788 * @constructor
35789 * Create a new ReaderLayout
35790 * @param {Object} config Configuration options
35791 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
35792 * document.body if omitted)
35793 */
35794 Roo.ReaderLayout = function(config, renderTo){
35795     var c = config || {size:{}};
35796     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
35797         north: c.north !== false ? Roo.apply({
35798             split:false,
35799             initialSize: 32,
35800             titlebar: false
35801         }, c.north) : false,
35802         west: c.west !== false ? Roo.apply({
35803             split:true,
35804             initialSize: 200,
35805             minSize: 175,
35806             maxSize: 400,
35807             titlebar: true,
35808             collapsible: true,
35809             animate: true,
35810             margins:{left:5,right:0,bottom:5,top:5},
35811             cmargins:{left:5,right:5,bottom:5,top:5}
35812         }, c.west) : false,
35813         east: c.east !== false ? Roo.apply({
35814             split:true,
35815             initialSize: 200,
35816             minSize: 175,
35817             maxSize: 400,
35818             titlebar: true,
35819             collapsible: true,
35820             animate: true,
35821             margins:{left:0,right:5,bottom:5,top:5},
35822             cmargins:{left:5,right:5,bottom:5,top:5}
35823         }, c.east) : false,
35824         center: Roo.apply({
35825             tabPosition: 'top',
35826             autoScroll:false,
35827             closeOnTab: true,
35828             titlebar:false,
35829             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
35830         }, c.center)
35831     });
35832
35833     this.el.addClass('x-reader');
35834
35835     this.beginUpdate();
35836
35837     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
35838         south: c.preview !== false ? Roo.apply({
35839             split:true,
35840             initialSize: 200,
35841             minSize: 100,
35842             autoScroll:true,
35843             collapsible:true,
35844             titlebar: true,
35845             cmargins:{top:5,left:0, right:0, bottom:0}
35846         }, c.preview) : false,
35847         center: Roo.apply({
35848             autoScroll:false,
35849             titlebar:false,
35850             minHeight:200
35851         }, c.listView)
35852     });
35853     this.add('center', new Roo.NestedLayoutPanel(inner,
35854             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
35855
35856     this.endUpdate();
35857
35858     this.regions.preview = inner.getRegion('south');
35859     this.regions.listView = inner.getRegion('center');
35860 };
35861
35862 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
35863  * Based on:
35864  * Ext JS Library 1.1.1
35865  * Copyright(c) 2006-2007, Ext JS, LLC.
35866  *
35867  * Originally Released Under LGPL - original licence link has changed is not relivant.
35868  *
35869  * Fork - LGPL
35870  * <script type="text/javascript">
35871  */
35872  
35873 /**
35874  * @class Roo.grid.Grid
35875  * @extends Roo.util.Observable
35876  * This class represents the primary interface of a component based grid control.
35877  * <br><br>Usage:<pre><code>
35878  var grid = new Roo.grid.Grid("my-container-id", {
35879      ds: myDataStore,
35880      cm: myColModel,
35881      selModel: mySelectionModel,
35882      autoSizeColumns: true,
35883      monitorWindowResize: false,
35884      trackMouseOver: true
35885  });
35886  // set any options
35887  grid.render();
35888  * </code></pre>
35889  * <b>Common Problems:</b><br/>
35890  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
35891  * element will correct this<br/>
35892  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
35893  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
35894  * are unpredictable.<br/>
35895  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
35896  * grid to calculate dimensions/offsets.<br/>
35897   * @constructor
35898  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
35899  * The container MUST have some type of size defined for the grid to fill. The container will be
35900  * automatically set to position relative if it isn't already.
35901  * @param {Object} config A config object that sets properties on this grid.
35902  */
35903 Roo.grid.Grid = function(container, config){
35904         // initialize the container
35905         this.container = Roo.get(container);
35906         this.container.update("");
35907         this.container.setStyle("overflow", "hidden");
35908     this.container.addClass('x-grid-container');
35909
35910     this.id = this.container.id;
35911
35912     Roo.apply(this, config);
35913     // check and correct shorthanded configs
35914     if(this.ds){
35915         this.dataSource = this.ds;
35916         delete this.ds;
35917     }
35918     if(this.cm){
35919         this.colModel = this.cm;
35920         delete this.cm;
35921     }
35922     if(this.sm){
35923         this.selModel = this.sm;
35924         delete this.sm;
35925     }
35926
35927     if (this.selModel) {
35928         this.selModel = Roo.factory(this.selModel, Roo.grid);
35929         this.sm = this.selModel;
35930         this.sm.xmodule = this.xmodule || false;
35931     }
35932     if (typeof(this.colModel.config) == 'undefined') {
35933         this.colModel = new Roo.grid.ColumnModel(this.colModel);
35934         this.cm = this.colModel;
35935         this.cm.xmodule = this.xmodule || false;
35936     }
35937     if (this.dataSource) {
35938         this.dataSource= Roo.factory(this.dataSource, Roo.data);
35939         this.ds = this.dataSource;
35940         this.ds.xmodule = this.xmodule || false;
35941          
35942     }
35943     
35944     
35945     
35946     if(this.width){
35947         this.container.setWidth(this.width);
35948     }
35949
35950     if(this.height){
35951         this.container.setHeight(this.height);
35952     }
35953     /** @private */
35954         this.addEvents({
35955         // raw events
35956         /**
35957          * @event click
35958          * The raw click event for the entire grid.
35959          * @param {Roo.EventObject} e
35960          */
35961         "click" : true,
35962         /**
35963          * @event dblclick
35964          * The raw dblclick event for the entire grid.
35965          * @param {Roo.EventObject} e
35966          */
35967         "dblclick" : true,
35968         /**
35969          * @event contextmenu
35970          * The raw contextmenu event for the entire grid.
35971          * @param {Roo.EventObject} e
35972          */
35973         "contextmenu" : true,
35974         /**
35975          * @event mousedown
35976          * The raw mousedown event for the entire grid.
35977          * @param {Roo.EventObject} e
35978          */
35979         "mousedown" : true,
35980         /**
35981          * @event mouseup
35982          * The raw mouseup event for the entire grid.
35983          * @param {Roo.EventObject} e
35984          */
35985         "mouseup" : true,
35986         /**
35987          * @event mouseover
35988          * The raw mouseover event for the entire grid.
35989          * @param {Roo.EventObject} e
35990          */
35991         "mouseover" : true,
35992         /**
35993          * @event mouseout
35994          * The raw mouseout event for the entire grid.
35995          * @param {Roo.EventObject} e
35996          */
35997         "mouseout" : true,
35998         /**
35999          * @event keypress
36000          * The raw keypress event for the entire grid.
36001          * @param {Roo.EventObject} e
36002          */
36003         "keypress" : true,
36004         /**
36005          * @event keydown
36006          * The raw keydown event for the entire grid.
36007          * @param {Roo.EventObject} e
36008          */
36009         "keydown" : true,
36010
36011         // custom events
36012
36013         /**
36014          * @event cellclick
36015          * Fires when a cell is clicked
36016          * @param {Grid} this
36017          * @param {Number} rowIndex
36018          * @param {Number} columnIndex
36019          * @param {Roo.EventObject} e
36020          */
36021         "cellclick" : true,
36022         /**
36023          * @event celldblclick
36024          * Fires when a cell is double clicked
36025          * @param {Grid} this
36026          * @param {Number} rowIndex
36027          * @param {Number} columnIndex
36028          * @param {Roo.EventObject} e
36029          */
36030         "celldblclick" : true,
36031         /**
36032          * @event rowclick
36033          * Fires when a row is clicked
36034          * @param {Grid} this
36035          * @param {Number} rowIndex
36036          * @param {Roo.EventObject} e
36037          */
36038         "rowclick" : true,
36039         /**
36040          * @event rowdblclick
36041          * Fires when a row is double clicked
36042          * @param {Grid} this
36043          * @param {Number} rowIndex
36044          * @param {Roo.EventObject} e
36045          */
36046         "rowdblclick" : true,
36047         /**
36048          * @event headerclick
36049          * Fires when a header is clicked
36050          * @param {Grid} this
36051          * @param {Number} columnIndex
36052          * @param {Roo.EventObject} e
36053          */
36054         "headerclick" : true,
36055         /**
36056          * @event headerdblclick
36057          * Fires when a header cell is double clicked
36058          * @param {Grid} this
36059          * @param {Number} columnIndex
36060          * @param {Roo.EventObject} e
36061          */
36062         "headerdblclick" : true,
36063         /**
36064          * @event rowcontextmenu
36065          * Fires when a row is right clicked
36066          * @param {Grid} this
36067          * @param {Number} rowIndex
36068          * @param {Roo.EventObject} e
36069          */
36070         "rowcontextmenu" : true,
36071         /**
36072          * @event cellcontextmenu
36073          * Fires when a cell is right clicked
36074          * @param {Grid} this
36075          * @param {Number} rowIndex
36076          * @param {Number} cellIndex
36077          * @param {Roo.EventObject} e
36078          */
36079          "cellcontextmenu" : true,
36080         /**
36081          * @event headercontextmenu
36082          * Fires when a header is right clicked
36083          * @param {Grid} this
36084          * @param {Number} columnIndex
36085          * @param {Roo.EventObject} e
36086          */
36087         "headercontextmenu" : true,
36088         /**
36089          * @event bodyscroll
36090          * Fires when the body element is scrolled
36091          * @param {Number} scrollLeft
36092          * @param {Number} scrollTop
36093          */
36094         "bodyscroll" : true,
36095         /**
36096          * @event columnresize
36097          * Fires when the user resizes a column
36098          * @param {Number} columnIndex
36099          * @param {Number} newSize
36100          */
36101         "columnresize" : true,
36102         /**
36103          * @event columnmove
36104          * Fires when the user moves a column
36105          * @param {Number} oldIndex
36106          * @param {Number} newIndex
36107          */
36108         "columnmove" : true,
36109         /**
36110          * @event startdrag
36111          * Fires when row(s) start being dragged
36112          * @param {Grid} this
36113          * @param {Roo.GridDD} dd The drag drop object
36114          * @param {event} e The raw browser event
36115          */
36116         "startdrag" : true,
36117         /**
36118          * @event enddrag
36119          * Fires when a drag operation is complete
36120          * @param {Grid} this
36121          * @param {Roo.GridDD} dd The drag drop object
36122          * @param {event} e The raw browser event
36123          */
36124         "enddrag" : true,
36125         /**
36126          * @event dragdrop
36127          * Fires when dragged row(s) are dropped on a valid DD target
36128          * @param {Grid} this
36129          * @param {Roo.GridDD} dd The drag drop object
36130          * @param {String} targetId The target drag drop object
36131          * @param {event} e The raw browser event
36132          */
36133         "dragdrop" : true,
36134         /**
36135          * @event dragover
36136          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
36137          * @param {Grid} this
36138          * @param {Roo.GridDD} dd The drag drop object
36139          * @param {String} targetId The target drag drop object
36140          * @param {event} e The raw browser event
36141          */
36142         "dragover" : true,
36143         /**
36144          * @event dragenter
36145          *  Fires when the dragged row(s) first cross another DD target while being dragged
36146          * @param {Grid} this
36147          * @param {Roo.GridDD} dd The drag drop object
36148          * @param {String} targetId The target drag drop object
36149          * @param {event} e The raw browser event
36150          */
36151         "dragenter" : true,
36152         /**
36153          * @event dragout
36154          * Fires when the dragged row(s) leave another DD target while being dragged
36155          * @param {Grid} this
36156          * @param {Roo.GridDD} dd The drag drop object
36157          * @param {String} targetId The target drag drop object
36158          * @param {event} e The raw browser event
36159          */
36160         "dragout" : true,
36161         /**
36162          * @event rowclass
36163          * Fires when a row is rendered, so you can change add a style to it.
36164          * @param {GridView} gridview   The grid view
36165          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
36166          */
36167         'rowclass' : true,
36168
36169         /**
36170          * @event render
36171          * Fires when the grid is rendered
36172          * @param {Grid} grid
36173          */
36174         'render' : true
36175     });
36176
36177     Roo.grid.Grid.superclass.constructor.call(this);
36178 };
36179 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
36180     
36181     /**
36182      * @cfg {String} ddGroup - drag drop group.
36183      */
36184
36185     /**
36186      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
36187      */
36188     minColumnWidth : 25,
36189
36190     /**
36191      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
36192      * <b>on initial render.</b> It is more efficient to explicitly size the columns
36193      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
36194      */
36195     autoSizeColumns : false,
36196
36197     /**
36198      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
36199      */
36200     autoSizeHeaders : true,
36201
36202     /**
36203      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
36204      */
36205     monitorWindowResize : true,
36206
36207     /**
36208      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
36209      * rows measured to get a columns size. Default is 0 (all rows).
36210      */
36211     maxRowsToMeasure : 0,
36212
36213     /**
36214      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
36215      */
36216     trackMouseOver : true,
36217
36218     /**
36219     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
36220     */
36221     
36222     /**
36223     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
36224     */
36225     enableDragDrop : false,
36226     
36227     /**
36228     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
36229     */
36230     enableColumnMove : true,
36231     
36232     /**
36233     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
36234     */
36235     enableColumnHide : true,
36236     
36237     /**
36238     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
36239     */
36240     enableRowHeightSync : false,
36241     
36242     /**
36243     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
36244     */
36245     stripeRows : true,
36246     
36247     /**
36248     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
36249     */
36250     autoHeight : false,
36251
36252     /**
36253      * @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.
36254      */
36255     autoExpandColumn : false,
36256
36257     /**
36258     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
36259     * Default is 50.
36260     */
36261     autoExpandMin : 50,
36262
36263     /**
36264     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
36265     */
36266     autoExpandMax : 1000,
36267
36268     /**
36269     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
36270     */
36271     view : null,
36272
36273     /**
36274     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
36275     */
36276     loadMask : false,
36277     /**
36278     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
36279     */
36280     dropTarget: false,
36281     
36282    
36283     
36284     // private
36285     rendered : false,
36286
36287     /**
36288     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
36289     * of a fixed width. Default is false.
36290     */
36291     /**
36292     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
36293     */
36294     /**
36295      * Called once after all setup has been completed and the grid is ready to be rendered.
36296      * @return {Roo.grid.Grid} this
36297      */
36298     render : function()
36299     {
36300         var c = this.container;
36301         // try to detect autoHeight/width mode
36302         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
36303             this.autoHeight = true;
36304         }
36305         var view = this.getView();
36306         view.init(this);
36307
36308         c.on("click", this.onClick, this);
36309         c.on("dblclick", this.onDblClick, this);
36310         c.on("contextmenu", this.onContextMenu, this);
36311         c.on("keydown", this.onKeyDown, this);
36312         if (Roo.isTouch) {
36313             c.on("touchstart", this.onTouchStart, this);
36314         }
36315
36316         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
36317
36318         this.getSelectionModel().init(this);
36319
36320         view.render();
36321
36322         if(this.loadMask){
36323             this.loadMask = new Roo.LoadMask(this.container,
36324                     Roo.apply({store:this.dataSource}, this.loadMask));
36325         }
36326         
36327         
36328         if (this.toolbar && this.toolbar.xtype) {
36329             this.toolbar.container = this.getView().getHeaderPanel(true);
36330             this.toolbar = new Roo.Toolbar(this.toolbar);
36331         }
36332         if (this.footer && this.footer.xtype) {
36333             this.footer.dataSource = this.getDataSource();
36334             this.footer.container = this.getView().getFooterPanel(true);
36335             this.footer = Roo.factory(this.footer, Roo);
36336         }
36337         if (this.dropTarget && this.dropTarget.xtype) {
36338             delete this.dropTarget.xtype;
36339             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
36340         }
36341         
36342         
36343         this.rendered = true;
36344         this.fireEvent('render', this);
36345         return this;
36346     },
36347
36348         /**
36349          * Reconfigures the grid to use a different Store and Column Model.
36350          * The View will be bound to the new objects and refreshed.
36351          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
36352          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
36353          */
36354     reconfigure : function(dataSource, colModel){
36355         if(this.loadMask){
36356             this.loadMask.destroy();
36357             this.loadMask = new Roo.LoadMask(this.container,
36358                     Roo.apply({store:dataSource}, this.loadMask));
36359         }
36360         this.view.bind(dataSource, colModel);
36361         this.dataSource = dataSource;
36362         this.colModel = colModel;
36363         this.view.refresh(true);
36364     },
36365
36366     // private
36367     onKeyDown : function(e){
36368         this.fireEvent("keydown", e);
36369     },
36370
36371     /**
36372      * Destroy this grid.
36373      * @param {Boolean} removeEl True to remove the element
36374      */
36375     destroy : function(removeEl, keepListeners){
36376         if(this.loadMask){
36377             this.loadMask.destroy();
36378         }
36379         var c = this.container;
36380         c.removeAllListeners();
36381         this.view.destroy();
36382         this.colModel.purgeListeners();
36383         if(!keepListeners){
36384             this.purgeListeners();
36385         }
36386         c.update("");
36387         if(removeEl === true){
36388             c.remove();
36389         }
36390     },
36391
36392     // private
36393     processEvent : function(name, e){
36394         // does this fire select???
36395         //Roo.log('grid:processEvent '  + name);
36396         
36397         if (name != 'touchstart' ) {
36398             this.fireEvent(name, e);    
36399         }
36400         
36401         var t = e.getTarget();
36402         var v = this.view;
36403         var header = v.findHeaderIndex(t);
36404         if(header !== false){
36405             var ename = name == 'touchstart' ? 'click' : name;
36406              
36407             this.fireEvent("header" + ename, this, header, e);
36408         }else{
36409             var row = v.findRowIndex(t);
36410             var cell = v.findCellIndex(t);
36411             if (name == 'touchstart') {
36412                 // first touch is always a click.
36413                 // hopefull this happens after selection is updated.?
36414                 name = false;
36415                 
36416                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
36417                     var cs = this.selModel.getSelectedCell();
36418                     if (row == cs[0] && cell == cs[1]){
36419                         name = 'dblclick';
36420                     }
36421                 }
36422                 if (typeof(this.selModel.getSelections) != 'undefined') {
36423                     var cs = this.selModel.getSelections();
36424                     var ds = this.dataSource;
36425                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
36426                         name = 'dblclick';
36427                     }
36428                 }
36429                 if (!name) {
36430                     return;
36431                 }
36432             }
36433             
36434             
36435             if(row !== false){
36436                 this.fireEvent("row" + name, this, row, e);
36437                 if(cell !== false){
36438                     this.fireEvent("cell" + name, this, row, cell, e);
36439                 }
36440             }
36441         }
36442     },
36443
36444     // private
36445     onClick : function(e){
36446         this.processEvent("click", e);
36447     },
36448    // private
36449     onTouchStart : function(e){
36450         this.processEvent("touchstart", e);
36451     },
36452
36453     // private
36454     onContextMenu : function(e, t){
36455         this.processEvent("contextmenu", e);
36456     },
36457
36458     // private
36459     onDblClick : function(e){
36460         this.processEvent("dblclick", e);
36461     },
36462
36463     // private
36464     walkCells : function(row, col, step, fn, scope){
36465         var cm = this.colModel, clen = cm.getColumnCount();
36466         var ds = this.dataSource, rlen = ds.getCount(), first = true;
36467         if(step < 0){
36468             if(col < 0){
36469                 row--;
36470                 first = false;
36471             }
36472             while(row >= 0){
36473                 if(!first){
36474                     col = clen-1;
36475                 }
36476                 first = false;
36477                 while(col >= 0){
36478                     if(fn.call(scope || this, row, col, cm) === true){
36479                         return [row, col];
36480                     }
36481                     col--;
36482                 }
36483                 row--;
36484             }
36485         } else {
36486             if(col >= clen){
36487                 row++;
36488                 first = false;
36489             }
36490             while(row < rlen){
36491                 if(!first){
36492                     col = 0;
36493                 }
36494                 first = false;
36495                 while(col < clen){
36496                     if(fn.call(scope || this, row, col, cm) === true){
36497                         return [row, col];
36498                     }
36499                     col++;
36500                 }
36501                 row++;
36502             }
36503         }
36504         return null;
36505     },
36506
36507     // private
36508     getSelections : function(){
36509         return this.selModel.getSelections();
36510     },
36511
36512     /**
36513      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
36514      * but if manual update is required this method will initiate it.
36515      */
36516     autoSize : function(){
36517         if(this.rendered){
36518             this.view.layout();
36519             if(this.view.adjustForScroll){
36520                 this.view.adjustForScroll();
36521             }
36522         }
36523     },
36524
36525     /**
36526      * Returns the grid's underlying element.
36527      * @return {Element} The element
36528      */
36529     getGridEl : function(){
36530         return this.container;
36531     },
36532
36533     // private for compatibility, overridden by editor grid
36534     stopEditing : function(){},
36535
36536     /**
36537      * Returns the grid's SelectionModel.
36538      * @return {SelectionModel}
36539      */
36540     getSelectionModel : function(){
36541         if(!this.selModel){
36542             this.selModel = new Roo.grid.RowSelectionModel();
36543         }
36544         return this.selModel;
36545     },
36546
36547     /**
36548      * Returns the grid's DataSource.
36549      * @return {DataSource}
36550      */
36551     getDataSource : function(){
36552         return this.dataSource;
36553     },
36554
36555     /**
36556      * Returns the grid's ColumnModel.
36557      * @return {ColumnModel}
36558      */
36559     getColumnModel : function(){
36560         return this.colModel;
36561     },
36562
36563     /**
36564      * Returns the grid's GridView object.
36565      * @return {GridView}
36566      */
36567     getView : function(){
36568         if(!this.view){
36569             this.view = new Roo.grid.GridView(this.viewConfig);
36570         }
36571         return this.view;
36572     },
36573     /**
36574      * Called to get grid's drag proxy text, by default returns this.ddText.
36575      * @return {String}
36576      */
36577     getDragDropText : function(){
36578         var count = this.selModel.getCount();
36579         return String.format(this.ddText, count, count == 1 ? '' : 's');
36580     }
36581 });
36582 /**
36583  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
36584  * %0 is replaced with the number of selected rows.
36585  * @type String
36586  */
36587 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
36588  * Based on:
36589  * Ext JS Library 1.1.1
36590  * Copyright(c) 2006-2007, Ext JS, LLC.
36591  *
36592  * Originally Released Under LGPL - original licence link has changed is not relivant.
36593  *
36594  * Fork - LGPL
36595  * <script type="text/javascript">
36596  */
36597  
36598 Roo.grid.AbstractGridView = function(){
36599         this.grid = null;
36600         
36601         this.events = {
36602             "beforerowremoved" : true,
36603             "beforerowsinserted" : true,
36604             "beforerefresh" : true,
36605             "rowremoved" : true,
36606             "rowsinserted" : true,
36607             "rowupdated" : true,
36608             "refresh" : true
36609         };
36610     Roo.grid.AbstractGridView.superclass.constructor.call(this);
36611 };
36612
36613 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
36614     rowClass : "x-grid-row",
36615     cellClass : "x-grid-cell",
36616     tdClass : "x-grid-td",
36617     hdClass : "x-grid-hd",
36618     splitClass : "x-grid-hd-split",
36619     
36620     init: function(grid){
36621         this.grid = grid;
36622                 var cid = this.grid.getGridEl().id;
36623         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
36624         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
36625         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
36626         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
36627         },
36628         
36629     getColumnRenderers : function(){
36630         var renderers = [];
36631         var cm = this.grid.colModel;
36632         var colCount = cm.getColumnCount();
36633         for(var i = 0; i < colCount; i++){
36634             renderers[i] = cm.getRenderer(i);
36635         }
36636         return renderers;
36637     },
36638     
36639     getColumnIds : function(){
36640         var ids = [];
36641         var cm = this.grid.colModel;
36642         var colCount = cm.getColumnCount();
36643         for(var i = 0; i < colCount; i++){
36644             ids[i] = cm.getColumnId(i);
36645         }
36646         return ids;
36647     },
36648     
36649     getDataIndexes : function(){
36650         if(!this.indexMap){
36651             this.indexMap = this.buildIndexMap();
36652         }
36653         return this.indexMap.colToData;
36654     },
36655     
36656     getColumnIndexByDataIndex : function(dataIndex){
36657         if(!this.indexMap){
36658             this.indexMap = this.buildIndexMap();
36659         }
36660         return this.indexMap.dataToCol[dataIndex];
36661     },
36662     
36663     /**
36664      * Set a css style for a column dynamically. 
36665      * @param {Number} colIndex The index of the column
36666      * @param {String} name The css property name
36667      * @param {String} value The css value
36668      */
36669     setCSSStyle : function(colIndex, name, value){
36670         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
36671         Roo.util.CSS.updateRule(selector, name, value);
36672     },
36673     
36674     generateRules : function(cm){
36675         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
36676         Roo.util.CSS.removeStyleSheet(rulesId);
36677         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36678             var cid = cm.getColumnId(i);
36679             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
36680                          this.tdSelector, cid, " {\n}\n",
36681                          this.hdSelector, cid, " {\n}\n",
36682                          this.splitSelector, cid, " {\n}\n");
36683         }
36684         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
36685     }
36686 });/*
36687  * Based on:
36688  * Ext JS Library 1.1.1
36689  * Copyright(c) 2006-2007, Ext JS, LLC.
36690  *
36691  * Originally Released Under LGPL - original licence link has changed is not relivant.
36692  *
36693  * Fork - LGPL
36694  * <script type="text/javascript">
36695  */
36696
36697 // private
36698 // This is a support class used internally by the Grid components
36699 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
36700     this.grid = grid;
36701     this.view = grid.getView();
36702     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36703     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
36704     if(hd2){
36705         this.setHandleElId(Roo.id(hd));
36706         this.setOuterHandleElId(Roo.id(hd2));
36707     }
36708     this.scroll = false;
36709 };
36710 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
36711     maxDragWidth: 120,
36712     getDragData : function(e){
36713         var t = Roo.lib.Event.getTarget(e);
36714         var h = this.view.findHeaderCell(t);
36715         if(h){
36716             return {ddel: h.firstChild, header:h};
36717         }
36718         return false;
36719     },
36720
36721     onInitDrag : function(e){
36722         this.view.headersDisabled = true;
36723         var clone = this.dragData.ddel.cloneNode(true);
36724         clone.id = Roo.id();
36725         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
36726         this.proxy.update(clone);
36727         return true;
36728     },
36729
36730     afterValidDrop : function(){
36731         var v = this.view;
36732         setTimeout(function(){
36733             v.headersDisabled = false;
36734         }, 50);
36735     },
36736
36737     afterInvalidDrop : function(){
36738         var v = this.view;
36739         setTimeout(function(){
36740             v.headersDisabled = false;
36741         }, 50);
36742     }
36743 });
36744 /*
36745  * Based on:
36746  * Ext JS Library 1.1.1
36747  * Copyright(c) 2006-2007, Ext JS, LLC.
36748  *
36749  * Originally Released Under LGPL - original licence link has changed is not relivant.
36750  *
36751  * Fork - LGPL
36752  * <script type="text/javascript">
36753  */
36754 // private
36755 // This is a support class used internally by the Grid components
36756 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
36757     this.grid = grid;
36758     this.view = grid.getView();
36759     // split the proxies so they don't interfere with mouse events
36760     this.proxyTop = Roo.DomHelper.append(document.body, {
36761         cls:"col-move-top", html:"&#160;"
36762     }, true);
36763     this.proxyBottom = Roo.DomHelper.append(document.body, {
36764         cls:"col-move-bottom", html:"&#160;"
36765     }, true);
36766     this.proxyTop.hide = this.proxyBottom.hide = function(){
36767         this.setLeftTop(-100,-100);
36768         this.setStyle("visibility", "hidden");
36769     };
36770     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36771     // temporarily disabled
36772     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
36773     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
36774 };
36775 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
36776     proxyOffsets : [-4, -9],
36777     fly: Roo.Element.fly,
36778
36779     getTargetFromEvent : function(e){
36780         var t = Roo.lib.Event.getTarget(e);
36781         var cindex = this.view.findCellIndex(t);
36782         if(cindex !== false){
36783             return this.view.getHeaderCell(cindex);
36784         }
36785         return null;
36786     },
36787
36788     nextVisible : function(h){
36789         var v = this.view, cm = this.grid.colModel;
36790         h = h.nextSibling;
36791         while(h){
36792             if(!cm.isHidden(v.getCellIndex(h))){
36793                 return h;
36794             }
36795             h = h.nextSibling;
36796         }
36797         return null;
36798     },
36799
36800     prevVisible : function(h){
36801         var v = this.view, cm = this.grid.colModel;
36802         h = h.prevSibling;
36803         while(h){
36804             if(!cm.isHidden(v.getCellIndex(h))){
36805                 return h;
36806             }
36807             h = h.prevSibling;
36808         }
36809         return null;
36810     },
36811
36812     positionIndicator : function(h, n, e){
36813         var x = Roo.lib.Event.getPageX(e);
36814         var r = Roo.lib.Dom.getRegion(n.firstChild);
36815         var px, pt, py = r.top + this.proxyOffsets[1];
36816         if((r.right - x) <= (r.right-r.left)/2){
36817             px = r.right+this.view.borderWidth;
36818             pt = "after";
36819         }else{
36820             px = r.left;
36821             pt = "before";
36822         }
36823         var oldIndex = this.view.getCellIndex(h);
36824         var newIndex = this.view.getCellIndex(n);
36825
36826         if(this.grid.colModel.isFixed(newIndex)){
36827             return false;
36828         }
36829
36830         var locked = this.grid.colModel.isLocked(newIndex);
36831
36832         if(pt == "after"){
36833             newIndex++;
36834         }
36835         if(oldIndex < newIndex){
36836             newIndex--;
36837         }
36838         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
36839             return false;
36840         }
36841         px +=  this.proxyOffsets[0];
36842         this.proxyTop.setLeftTop(px, py);
36843         this.proxyTop.show();
36844         if(!this.bottomOffset){
36845             this.bottomOffset = this.view.mainHd.getHeight();
36846         }
36847         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
36848         this.proxyBottom.show();
36849         return pt;
36850     },
36851
36852     onNodeEnter : function(n, dd, e, data){
36853         if(data.header != n){
36854             this.positionIndicator(data.header, n, e);
36855         }
36856     },
36857
36858     onNodeOver : function(n, dd, e, data){
36859         var result = false;
36860         if(data.header != n){
36861             result = this.positionIndicator(data.header, n, e);
36862         }
36863         if(!result){
36864             this.proxyTop.hide();
36865             this.proxyBottom.hide();
36866         }
36867         return result ? this.dropAllowed : this.dropNotAllowed;
36868     },
36869
36870     onNodeOut : function(n, dd, e, data){
36871         this.proxyTop.hide();
36872         this.proxyBottom.hide();
36873     },
36874
36875     onNodeDrop : function(n, dd, e, data){
36876         var h = data.header;
36877         if(h != n){
36878             var cm = this.grid.colModel;
36879             var x = Roo.lib.Event.getPageX(e);
36880             var r = Roo.lib.Dom.getRegion(n.firstChild);
36881             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
36882             var oldIndex = this.view.getCellIndex(h);
36883             var newIndex = this.view.getCellIndex(n);
36884             var locked = cm.isLocked(newIndex);
36885             if(pt == "after"){
36886                 newIndex++;
36887             }
36888             if(oldIndex < newIndex){
36889                 newIndex--;
36890             }
36891             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
36892                 return false;
36893             }
36894             cm.setLocked(oldIndex, locked, true);
36895             cm.moveColumn(oldIndex, newIndex);
36896             this.grid.fireEvent("columnmove", oldIndex, newIndex);
36897             return true;
36898         }
36899         return false;
36900     }
36901 });
36902 /*
36903  * Based on:
36904  * Ext JS Library 1.1.1
36905  * Copyright(c) 2006-2007, Ext JS, LLC.
36906  *
36907  * Originally Released Under LGPL - original licence link has changed is not relivant.
36908  *
36909  * Fork - LGPL
36910  * <script type="text/javascript">
36911  */
36912   
36913 /**
36914  * @class Roo.grid.GridView
36915  * @extends Roo.util.Observable
36916  *
36917  * @constructor
36918  * @param {Object} config
36919  */
36920 Roo.grid.GridView = function(config){
36921     Roo.grid.GridView.superclass.constructor.call(this);
36922     this.el = null;
36923
36924     Roo.apply(this, config);
36925 };
36926
36927 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
36928
36929     unselectable :  'unselectable="on"',
36930     unselectableCls :  'x-unselectable',
36931     
36932     
36933     rowClass : "x-grid-row",
36934
36935     cellClass : "x-grid-col",
36936
36937     tdClass : "x-grid-td",
36938
36939     hdClass : "x-grid-hd",
36940
36941     splitClass : "x-grid-split",
36942
36943     sortClasses : ["sort-asc", "sort-desc"],
36944
36945     enableMoveAnim : false,
36946
36947     hlColor: "C3DAF9",
36948
36949     dh : Roo.DomHelper,
36950
36951     fly : Roo.Element.fly,
36952
36953     css : Roo.util.CSS,
36954
36955     borderWidth: 1,
36956
36957     splitOffset: 3,
36958
36959     scrollIncrement : 22,
36960
36961     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
36962
36963     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
36964
36965     bind : function(ds, cm){
36966         if(this.ds){
36967             this.ds.un("load", this.onLoad, this);
36968             this.ds.un("datachanged", this.onDataChange, this);
36969             this.ds.un("add", this.onAdd, this);
36970             this.ds.un("remove", this.onRemove, this);
36971             this.ds.un("update", this.onUpdate, this);
36972             this.ds.un("clear", this.onClear, this);
36973         }
36974         if(ds){
36975             ds.on("load", this.onLoad, this);
36976             ds.on("datachanged", this.onDataChange, this);
36977             ds.on("add", this.onAdd, this);
36978             ds.on("remove", this.onRemove, this);
36979             ds.on("update", this.onUpdate, this);
36980             ds.on("clear", this.onClear, this);
36981         }
36982         this.ds = ds;
36983
36984         if(this.cm){
36985             this.cm.un("widthchange", this.onColWidthChange, this);
36986             this.cm.un("headerchange", this.onHeaderChange, this);
36987             this.cm.un("hiddenchange", this.onHiddenChange, this);
36988             this.cm.un("columnmoved", this.onColumnMove, this);
36989             this.cm.un("columnlockchange", this.onColumnLock, this);
36990         }
36991         if(cm){
36992             this.generateRules(cm);
36993             cm.on("widthchange", this.onColWidthChange, this);
36994             cm.on("headerchange", this.onHeaderChange, this);
36995             cm.on("hiddenchange", this.onHiddenChange, this);
36996             cm.on("columnmoved", this.onColumnMove, this);
36997             cm.on("columnlockchange", this.onColumnLock, this);
36998         }
36999         this.cm = cm;
37000     },
37001
37002     init: function(grid){
37003         Roo.grid.GridView.superclass.init.call(this, grid);
37004
37005         this.bind(grid.dataSource, grid.colModel);
37006
37007         grid.on("headerclick", this.handleHeaderClick, this);
37008
37009         if(grid.trackMouseOver){
37010             grid.on("mouseover", this.onRowOver, this);
37011             grid.on("mouseout", this.onRowOut, this);
37012         }
37013         grid.cancelTextSelection = function(){};
37014         this.gridId = grid.id;
37015
37016         var tpls = this.templates || {};
37017
37018         if(!tpls.master){
37019             tpls.master = new Roo.Template(
37020                '<div class="x-grid" hidefocus="true">',
37021                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
37022                   '<div class="x-grid-topbar"></div>',
37023                   '<div class="x-grid-scroller"><div></div></div>',
37024                   '<div class="x-grid-locked">',
37025                       '<div class="x-grid-header">{lockedHeader}</div>',
37026                       '<div class="x-grid-body">{lockedBody}</div>',
37027                   "</div>",
37028                   '<div class="x-grid-viewport">',
37029                       '<div class="x-grid-header">{header}</div>',
37030                       '<div class="x-grid-body">{body}</div>',
37031                   "</div>",
37032                   '<div class="x-grid-bottombar"></div>',
37033                  
37034                   '<div class="x-grid-resize-proxy">&#160;</div>',
37035                "</div>"
37036             );
37037             tpls.master.disableformats = true;
37038         }
37039
37040         if(!tpls.header){
37041             tpls.header = new Roo.Template(
37042                '<table border="0" cellspacing="0" cellpadding="0">',
37043                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
37044                "</table>{splits}"
37045             );
37046             tpls.header.disableformats = true;
37047         }
37048         tpls.header.compile();
37049
37050         if(!tpls.hcell){
37051             tpls.hcell = new Roo.Template(
37052                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
37053                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
37054                 "</div></td>"
37055              );
37056              tpls.hcell.disableFormats = true;
37057         }
37058         tpls.hcell.compile();
37059
37060         if(!tpls.hsplit){
37061             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
37062                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
37063             tpls.hsplit.disableFormats = true;
37064         }
37065         tpls.hsplit.compile();
37066
37067         if(!tpls.body){
37068             tpls.body = new Roo.Template(
37069                '<table border="0" cellspacing="0" cellpadding="0">',
37070                "<tbody>{rows}</tbody>",
37071                "</table>"
37072             );
37073             tpls.body.disableFormats = true;
37074         }
37075         tpls.body.compile();
37076
37077         if(!tpls.row){
37078             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
37079             tpls.row.disableFormats = true;
37080         }
37081         tpls.row.compile();
37082
37083         if(!tpls.cell){
37084             tpls.cell = new Roo.Template(
37085                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
37086                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
37087                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
37088                 "</td>"
37089             );
37090             tpls.cell.disableFormats = true;
37091         }
37092         tpls.cell.compile();
37093
37094         this.templates = tpls;
37095     },
37096
37097     // remap these for backwards compat
37098     onColWidthChange : function(){
37099         this.updateColumns.apply(this, arguments);
37100     },
37101     onHeaderChange : function(){
37102         this.updateHeaders.apply(this, arguments);
37103     }, 
37104     onHiddenChange : function(){
37105         this.handleHiddenChange.apply(this, arguments);
37106     },
37107     onColumnMove : function(){
37108         this.handleColumnMove.apply(this, arguments);
37109     },
37110     onColumnLock : function(){
37111         this.handleLockChange.apply(this, arguments);
37112     },
37113
37114     onDataChange : function(){
37115         this.refresh();
37116         this.updateHeaderSortState();
37117     },
37118
37119     onClear : function(){
37120         this.refresh();
37121     },
37122
37123     onUpdate : function(ds, record){
37124         this.refreshRow(record);
37125     },
37126
37127     refreshRow : function(record){
37128         var ds = this.ds, index;
37129         if(typeof record == 'number'){
37130             index = record;
37131             record = ds.getAt(index);
37132         }else{
37133             index = ds.indexOf(record);
37134         }
37135         this.insertRows(ds, index, index, true);
37136         this.onRemove(ds, record, index+1, true);
37137         this.syncRowHeights(index, index);
37138         this.layout();
37139         this.fireEvent("rowupdated", this, index, record);
37140     },
37141
37142     onAdd : function(ds, records, index){
37143         this.insertRows(ds, index, index + (records.length-1));
37144     },
37145
37146     onRemove : function(ds, record, index, isUpdate){
37147         if(isUpdate !== true){
37148             this.fireEvent("beforerowremoved", this, index, record);
37149         }
37150         var bt = this.getBodyTable(), lt = this.getLockedTable();
37151         if(bt.rows[index]){
37152             bt.firstChild.removeChild(bt.rows[index]);
37153         }
37154         if(lt.rows[index]){
37155             lt.firstChild.removeChild(lt.rows[index]);
37156         }
37157         if(isUpdate !== true){
37158             this.stripeRows(index);
37159             this.syncRowHeights(index, index);
37160             this.layout();
37161             this.fireEvent("rowremoved", this, index, record);
37162         }
37163     },
37164
37165     onLoad : function(){
37166         this.scrollToTop();
37167     },
37168
37169     /**
37170      * Scrolls the grid to the top
37171      */
37172     scrollToTop : function(){
37173         if(this.scroller){
37174             this.scroller.dom.scrollTop = 0;
37175             this.syncScroll();
37176         }
37177     },
37178
37179     /**
37180      * Gets a panel in the header of the grid that can be used for toolbars etc.
37181      * After modifying the contents of this panel a call to grid.autoSize() may be
37182      * required to register any changes in size.
37183      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
37184      * @return Roo.Element
37185      */
37186     getHeaderPanel : function(doShow){
37187         if(doShow){
37188             this.headerPanel.show();
37189         }
37190         return this.headerPanel;
37191     },
37192
37193     /**
37194      * Gets a panel in the footer of the grid that can be used for toolbars etc.
37195      * After modifying the contents of this panel a call to grid.autoSize() may be
37196      * required to register any changes in size.
37197      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
37198      * @return Roo.Element
37199      */
37200     getFooterPanel : function(doShow){
37201         if(doShow){
37202             this.footerPanel.show();
37203         }
37204         return this.footerPanel;
37205     },
37206
37207     initElements : function(){
37208         var E = Roo.Element;
37209         var el = this.grid.getGridEl().dom.firstChild;
37210         var cs = el.childNodes;
37211
37212         this.el = new E(el);
37213         
37214          this.focusEl = new E(el.firstChild);
37215         this.focusEl.swallowEvent("click", true);
37216         
37217         this.headerPanel = new E(cs[1]);
37218         this.headerPanel.enableDisplayMode("block");
37219
37220         this.scroller = new E(cs[2]);
37221         this.scrollSizer = new E(this.scroller.dom.firstChild);
37222
37223         this.lockedWrap = new E(cs[3]);
37224         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
37225         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
37226
37227         this.mainWrap = new E(cs[4]);
37228         this.mainHd = new E(this.mainWrap.dom.firstChild);
37229         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
37230
37231         this.footerPanel = new E(cs[5]);
37232         this.footerPanel.enableDisplayMode("block");
37233
37234         this.resizeProxy = new E(cs[6]);
37235
37236         this.headerSelector = String.format(
37237            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
37238            this.lockedHd.id, this.mainHd.id
37239         );
37240
37241         this.splitterSelector = String.format(
37242            '#{0} div.x-grid-split, #{1} div.x-grid-split',
37243            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
37244         );
37245     },
37246     idToCssName : function(s)
37247     {
37248         return s.replace(/[^a-z0-9]+/ig, '-');
37249     },
37250
37251     getHeaderCell : function(index){
37252         return Roo.DomQuery.select(this.headerSelector)[index];
37253     },
37254
37255     getHeaderCellMeasure : function(index){
37256         return this.getHeaderCell(index).firstChild;
37257     },
37258
37259     getHeaderCellText : function(index){
37260         return this.getHeaderCell(index).firstChild.firstChild;
37261     },
37262
37263     getLockedTable : function(){
37264         return this.lockedBody.dom.firstChild;
37265     },
37266
37267     getBodyTable : function(){
37268         return this.mainBody.dom.firstChild;
37269     },
37270
37271     getLockedRow : function(index){
37272         return this.getLockedTable().rows[index];
37273     },
37274
37275     getRow : function(index){
37276         return this.getBodyTable().rows[index];
37277     },
37278
37279     getRowComposite : function(index){
37280         if(!this.rowEl){
37281             this.rowEl = new Roo.CompositeElementLite();
37282         }
37283         var els = [], lrow, mrow;
37284         if(lrow = this.getLockedRow(index)){
37285             els.push(lrow);
37286         }
37287         if(mrow = this.getRow(index)){
37288             els.push(mrow);
37289         }
37290         this.rowEl.elements = els;
37291         return this.rowEl;
37292     },
37293     /**
37294      * Gets the 'td' of the cell
37295      * 
37296      * @param {Integer} rowIndex row to select
37297      * @param {Integer} colIndex column to select
37298      * 
37299      * @return {Object} 
37300      */
37301     getCell : function(rowIndex, colIndex){
37302         var locked = this.cm.getLockedCount();
37303         var source;
37304         if(colIndex < locked){
37305             source = this.lockedBody.dom.firstChild;
37306         }else{
37307             source = this.mainBody.dom.firstChild;
37308             colIndex -= locked;
37309         }
37310         return source.rows[rowIndex].childNodes[colIndex];
37311     },
37312
37313     getCellText : function(rowIndex, colIndex){
37314         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
37315     },
37316
37317     getCellBox : function(cell){
37318         var b = this.fly(cell).getBox();
37319         if(Roo.isOpera){ // opera fails to report the Y
37320             b.y = cell.offsetTop + this.mainBody.getY();
37321         }
37322         return b;
37323     },
37324
37325     getCellIndex : function(cell){
37326         var id = String(cell.className).match(this.cellRE);
37327         if(id){
37328             return parseInt(id[1], 10);
37329         }
37330         return 0;
37331     },
37332
37333     findHeaderIndex : function(n){
37334         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
37335         return r ? this.getCellIndex(r) : false;
37336     },
37337
37338     findHeaderCell : function(n){
37339         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
37340         return r ? r : false;
37341     },
37342
37343     findRowIndex : function(n){
37344         if(!n){
37345             return false;
37346         }
37347         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
37348         return r ? r.rowIndex : false;
37349     },
37350
37351     findCellIndex : function(node){
37352         var stop = this.el.dom;
37353         while(node && node != stop){
37354             if(this.findRE.test(node.className)){
37355                 return this.getCellIndex(node);
37356             }
37357             node = node.parentNode;
37358         }
37359         return false;
37360     },
37361
37362     getColumnId : function(index){
37363         return this.cm.getColumnId(index);
37364     },
37365
37366     getSplitters : function()
37367     {
37368         if(this.splitterSelector){
37369            return Roo.DomQuery.select(this.splitterSelector);
37370         }else{
37371             return null;
37372       }
37373     },
37374
37375     getSplitter : function(index){
37376         return this.getSplitters()[index];
37377     },
37378
37379     onRowOver : function(e, t){
37380         var row;
37381         if((row = this.findRowIndex(t)) !== false){
37382             this.getRowComposite(row).addClass("x-grid-row-over");
37383         }
37384     },
37385
37386     onRowOut : function(e, t){
37387         var row;
37388         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
37389             this.getRowComposite(row).removeClass("x-grid-row-over");
37390         }
37391     },
37392
37393     renderHeaders : function(){
37394         var cm = this.cm;
37395         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
37396         var cb = [], lb = [], sb = [], lsb = [], p = {};
37397         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37398             p.cellId = "x-grid-hd-0-" + i;
37399             p.splitId = "x-grid-csplit-0-" + i;
37400             p.id = cm.getColumnId(i);
37401             p.title = cm.getColumnTooltip(i) || "";
37402             p.value = cm.getColumnHeader(i) || "";
37403             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
37404             if(!cm.isLocked(i)){
37405                 cb[cb.length] = ct.apply(p);
37406                 sb[sb.length] = st.apply(p);
37407             }else{
37408                 lb[lb.length] = ct.apply(p);
37409                 lsb[lsb.length] = st.apply(p);
37410             }
37411         }
37412         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
37413                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
37414     },
37415
37416     updateHeaders : function(){
37417         var html = this.renderHeaders();
37418         this.lockedHd.update(html[0]);
37419         this.mainHd.update(html[1]);
37420     },
37421
37422     /**
37423      * Focuses the specified row.
37424      * @param {Number} row The row index
37425      */
37426     focusRow : function(row)
37427     {
37428         //Roo.log('GridView.focusRow');
37429         var x = this.scroller.dom.scrollLeft;
37430         this.focusCell(row, 0, false);
37431         this.scroller.dom.scrollLeft = x;
37432     },
37433
37434     /**
37435      * Focuses the specified cell.
37436      * @param {Number} row The row index
37437      * @param {Number} col The column index
37438      * @param {Boolean} hscroll false to disable horizontal scrolling
37439      */
37440     focusCell : function(row, col, hscroll)
37441     {
37442         //Roo.log('GridView.focusCell');
37443         var el = this.ensureVisible(row, col, hscroll);
37444         this.focusEl.alignTo(el, "tl-tl");
37445         if(Roo.isGecko){
37446             this.focusEl.focus();
37447         }else{
37448             this.focusEl.focus.defer(1, this.focusEl);
37449         }
37450     },
37451
37452     /**
37453      * Scrolls the specified cell into view
37454      * @param {Number} row The row index
37455      * @param {Number} col The column index
37456      * @param {Boolean} hscroll false to disable horizontal scrolling
37457      */
37458     ensureVisible : function(row, col, hscroll)
37459     {
37460         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
37461         //return null; //disable for testing.
37462         if(typeof row != "number"){
37463             row = row.rowIndex;
37464         }
37465         if(row < 0 && row >= this.ds.getCount()){
37466             return  null;
37467         }
37468         col = (col !== undefined ? col : 0);
37469         var cm = this.grid.colModel;
37470         while(cm.isHidden(col)){
37471             col++;
37472         }
37473
37474         var el = this.getCell(row, col);
37475         if(!el){
37476             return null;
37477         }
37478         var c = this.scroller.dom;
37479
37480         var ctop = parseInt(el.offsetTop, 10);
37481         var cleft = parseInt(el.offsetLeft, 10);
37482         var cbot = ctop + el.offsetHeight;
37483         var cright = cleft + el.offsetWidth;
37484         
37485         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
37486         var stop = parseInt(c.scrollTop, 10);
37487         var sleft = parseInt(c.scrollLeft, 10);
37488         var sbot = stop + ch;
37489         var sright = sleft + c.clientWidth;
37490         /*
37491         Roo.log('GridView.ensureVisible:' +
37492                 ' ctop:' + ctop +
37493                 ' c.clientHeight:' + c.clientHeight +
37494                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
37495                 ' stop:' + stop +
37496                 ' cbot:' + cbot +
37497                 ' sbot:' + sbot +
37498                 ' ch:' + ch  
37499                 );
37500         */
37501         if(ctop < stop){
37502              c.scrollTop = ctop;
37503             //Roo.log("set scrolltop to ctop DISABLE?");
37504         }else if(cbot > sbot){
37505             //Roo.log("set scrolltop to cbot-ch");
37506             c.scrollTop = cbot-ch;
37507         }
37508         
37509         if(hscroll !== false){
37510             if(cleft < sleft){
37511                 c.scrollLeft = cleft;
37512             }else if(cright > sright){
37513                 c.scrollLeft = cright-c.clientWidth;
37514             }
37515         }
37516          
37517         return el;
37518     },
37519
37520     updateColumns : function(){
37521         this.grid.stopEditing();
37522         var cm = this.grid.colModel, colIds = this.getColumnIds();
37523         //var totalWidth = cm.getTotalWidth();
37524         var pos = 0;
37525         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37526             //if(cm.isHidden(i)) continue;
37527             var w = cm.getColumnWidth(i);
37528             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37529             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37530         }
37531         this.updateSplitters();
37532     },
37533
37534     generateRules : function(cm){
37535         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
37536         Roo.util.CSS.removeStyleSheet(rulesId);
37537         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37538             var cid = cm.getColumnId(i);
37539             var align = '';
37540             if(cm.config[i].align){
37541                 align = 'text-align:'+cm.config[i].align+';';
37542             }
37543             var hidden = '';
37544             if(cm.isHidden(i)){
37545                 hidden = 'display:none;';
37546             }
37547             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
37548             ruleBuf.push(
37549                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
37550                     this.hdSelector, cid, " {\n", align, width, "}\n",
37551                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
37552                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
37553         }
37554         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37555     },
37556
37557     updateSplitters : function(){
37558         var cm = this.cm, s = this.getSplitters();
37559         if(s){ // splitters not created yet
37560             var pos = 0, locked = true;
37561             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37562                 if(cm.isHidden(i)) continue;
37563                 var w = cm.getColumnWidth(i); // make sure it's a number
37564                 if(!cm.isLocked(i) && locked){
37565                     pos = 0;
37566                     locked = false;
37567                 }
37568                 pos += w;
37569                 s[i].style.left = (pos-this.splitOffset) + "px";
37570             }
37571         }
37572     },
37573
37574     handleHiddenChange : function(colModel, colIndex, hidden){
37575         if(hidden){
37576             this.hideColumn(colIndex);
37577         }else{
37578             this.unhideColumn(colIndex);
37579         }
37580     },
37581
37582     hideColumn : function(colIndex){
37583         var cid = this.getColumnId(colIndex);
37584         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
37585         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
37586         if(Roo.isSafari){
37587             this.updateHeaders();
37588         }
37589         this.updateSplitters();
37590         this.layout();
37591     },
37592
37593     unhideColumn : function(colIndex){
37594         var cid = this.getColumnId(colIndex);
37595         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
37596         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
37597
37598         if(Roo.isSafari){
37599             this.updateHeaders();
37600         }
37601         this.updateSplitters();
37602         this.layout();
37603     },
37604
37605     insertRows : function(dm, firstRow, lastRow, isUpdate){
37606         if(firstRow == 0 && lastRow == dm.getCount()-1){
37607             this.refresh();
37608         }else{
37609             if(!isUpdate){
37610                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
37611             }
37612             var s = this.getScrollState();
37613             var markup = this.renderRows(firstRow, lastRow);
37614             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
37615             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
37616             this.restoreScroll(s);
37617             if(!isUpdate){
37618                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
37619                 this.syncRowHeights(firstRow, lastRow);
37620                 this.stripeRows(firstRow);
37621                 this.layout();
37622             }
37623         }
37624     },
37625
37626     bufferRows : function(markup, target, index){
37627         var before = null, trows = target.rows, tbody = target.tBodies[0];
37628         if(index < trows.length){
37629             before = trows[index];
37630         }
37631         var b = document.createElement("div");
37632         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
37633         var rows = b.firstChild.rows;
37634         for(var i = 0, len = rows.length; i < len; i++){
37635             if(before){
37636                 tbody.insertBefore(rows[0], before);
37637             }else{
37638                 tbody.appendChild(rows[0]);
37639             }
37640         }
37641         b.innerHTML = "";
37642         b = null;
37643     },
37644
37645     deleteRows : function(dm, firstRow, lastRow){
37646         if(dm.getRowCount()<1){
37647             this.fireEvent("beforerefresh", this);
37648             this.mainBody.update("");
37649             this.lockedBody.update("");
37650             this.fireEvent("refresh", this);
37651         }else{
37652             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
37653             var bt = this.getBodyTable();
37654             var tbody = bt.firstChild;
37655             var rows = bt.rows;
37656             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
37657                 tbody.removeChild(rows[firstRow]);
37658             }
37659             this.stripeRows(firstRow);
37660             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
37661         }
37662     },
37663
37664     updateRows : function(dataSource, firstRow, lastRow){
37665         var s = this.getScrollState();
37666         this.refresh();
37667         this.restoreScroll(s);
37668     },
37669
37670     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
37671         if(!noRefresh){
37672            this.refresh();
37673         }
37674         this.updateHeaderSortState();
37675     },
37676
37677     getScrollState : function(){
37678         
37679         var sb = this.scroller.dom;
37680         return {left: sb.scrollLeft, top: sb.scrollTop};
37681     },
37682
37683     stripeRows : function(startRow){
37684         if(!this.grid.stripeRows || this.ds.getCount() < 1){
37685             return;
37686         }
37687         startRow = startRow || 0;
37688         var rows = this.getBodyTable().rows;
37689         var lrows = this.getLockedTable().rows;
37690         var cls = ' x-grid-row-alt ';
37691         for(var i = startRow, len = rows.length; i < len; i++){
37692             var row = rows[i], lrow = lrows[i];
37693             var isAlt = ((i+1) % 2 == 0);
37694             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
37695             if(isAlt == hasAlt){
37696                 continue;
37697             }
37698             if(isAlt){
37699                 row.className += " x-grid-row-alt";
37700             }else{
37701                 row.className = row.className.replace("x-grid-row-alt", "");
37702             }
37703             if(lrow){
37704                 lrow.className = row.className;
37705             }
37706         }
37707     },
37708
37709     restoreScroll : function(state){
37710         //Roo.log('GridView.restoreScroll');
37711         var sb = this.scroller.dom;
37712         sb.scrollLeft = state.left;
37713         sb.scrollTop = state.top;
37714         this.syncScroll();
37715     },
37716
37717     syncScroll : function(){
37718         //Roo.log('GridView.syncScroll');
37719         var sb = this.scroller.dom;
37720         var sh = this.mainHd.dom;
37721         var bs = this.mainBody.dom;
37722         var lv = this.lockedBody.dom;
37723         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
37724         lv.scrollTop = bs.scrollTop = sb.scrollTop;
37725     },
37726
37727     handleScroll : function(e){
37728         this.syncScroll();
37729         var sb = this.scroller.dom;
37730         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
37731         e.stopEvent();
37732     },
37733
37734     handleWheel : function(e){
37735         var d = e.getWheelDelta();
37736         this.scroller.dom.scrollTop -= d*22;
37737         // set this here to prevent jumpy scrolling on large tables
37738         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
37739         e.stopEvent();
37740     },
37741
37742     renderRows : function(startRow, endRow){
37743         // pull in all the crap needed to render rows
37744         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
37745         var colCount = cm.getColumnCount();
37746
37747         if(ds.getCount() < 1){
37748             return ["", ""];
37749         }
37750
37751         // build a map for all the columns
37752         var cs = [];
37753         for(var i = 0; i < colCount; i++){
37754             var name = cm.getDataIndex(i);
37755             cs[i] = {
37756                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
37757                 renderer : cm.getRenderer(i),
37758                 id : cm.getColumnId(i),
37759                 locked : cm.isLocked(i)
37760             };
37761         }
37762
37763         startRow = startRow || 0;
37764         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
37765
37766         // records to render
37767         var rs = ds.getRange(startRow, endRow);
37768
37769         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
37770     },
37771
37772     // As much as I hate to duplicate code, this was branched because FireFox really hates
37773     // [].join("") on strings. The performance difference was substantial enough to
37774     // branch this function
37775     doRender : Roo.isGecko ?
37776             function(cs, rs, ds, startRow, colCount, stripe){
37777                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37778                 // buffers
37779                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37780                 
37781                 var hasListener = this.grid.hasListener('rowclass');
37782                 var rowcfg = {};
37783                 for(var j = 0, len = rs.length; j < len; j++){
37784                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
37785                     for(var i = 0; i < colCount; i++){
37786                         c = cs[i];
37787                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37788                         p.id = c.id;
37789                         p.css = p.attr = "";
37790                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37791                         if(p.value == undefined || p.value === "") p.value = "&#160;";
37792                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37793                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37794                         }
37795                         var markup = ct.apply(p);
37796                         if(!c.locked){
37797                             cb+= markup;
37798                         }else{
37799                             lcb+= markup;
37800                         }
37801                     }
37802                     var alt = [];
37803                     if(stripe && ((rowIndex+1) % 2 == 0)){
37804                         alt.push("x-grid-row-alt")
37805                     }
37806                     if(r.dirty){
37807                         alt.push(  " x-grid-dirty-row");
37808                     }
37809                     rp.cells = lcb;
37810                     if(this.getRowClass){
37811                         alt.push(this.getRowClass(r, rowIndex));
37812                     }
37813                     if (hasListener) {
37814                         rowcfg = {
37815                              
37816                             record: r,
37817                             rowIndex : rowIndex,
37818                             rowClass : ''
37819                         };
37820                         this.grid.fireEvent('rowclass', this, rowcfg);
37821                         alt.push(rowcfg.rowClass);
37822                     }
37823                     rp.alt = alt.join(" ");
37824                     lbuf+= rt.apply(rp);
37825                     rp.cells = cb;
37826                     buf+=  rt.apply(rp);
37827                 }
37828                 return [lbuf, buf];
37829             } :
37830             function(cs, rs, ds, startRow, colCount, stripe){
37831                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37832                 // buffers
37833                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37834                 var hasListener = this.grid.hasListener('rowclass');
37835  
37836                 var rowcfg = {};
37837                 for(var j = 0, len = rs.length; j < len; j++){
37838                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
37839                     for(var i = 0; i < colCount; i++){
37840                         c = cs[i];
37841                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37842                         p.id = c.id;
37843                         p.css = p.attr = "";
37844                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37845                         if(p.value == undefined || p.value === "") p.value = "&#160;";
37846                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37847                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37848                         }
37849                         
37850                         var markup = ct.apply(p);
37851                         if(!c.locked){
37852                             cb[cb.length] = markup;
37853                         }else{
37854                             lcb[lcb.length] = markup;
37855                         }
37856                     }
37857                     var alt = [];
37858                     if(stripe && ((rowIndex+1) % 2 == 0)){
37859                         alt.push( "x-grid-row-alt");
37860                     }
37861                     if(r.dirty){
37862                         alt.push(" x-grid-dirty-row");
37863                     }
37864                     rp.cells = lcb;
37865                     if(this.getRowClass){
37866                         alt.push( this.getRowClass(r, rowIndex));
37867                     }
37868                     if (hasListener) {
37869                         rowcfg = {
37870                              
37871                             record: r,
37872                             rowIndex : rowIndex,
37873                             rowClass : ''
37874                         };
37875                         this.grid.fireEvent('rowclass', this, rowcfg);
37876                         alt.push(rowcfg.rowClass);
37877                     }
37878                     rp.alt = alt.join(" ");
37879                     rp.cells = lcb.join("");
37880                     lbuf[lbuf.length] = rt.apply(rp);
37881                     rp.cells = cb.join("");
37882                     buf[buf.length] =  rt.apply(rp);
37883                 }
37884                 return [lbuf.join(""), buf.join("")];
37885             },
37886
37887     renderBody : function(){
37888         var markup = this.renderRows();
37889         var bt = this.templates.body;
37890         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
37891     },
37892
37893     /**
37894      * Refreshes the grid
37895      * @param {Boolean} headersToo
37896      */
37897     refresh : function(headersToo){
37898         this.fireEvent("beforerefresh", this);
37899         this.grid.stopEditing();
37900         var result = this.renderBody();
37901         this.lockedBody.update(result[0]);
37902         this.mainBody.update(result[1]);
37903         if(headersToo === true){
37904             this.updateHeaders();
37905             this.updateColumns();
37906             this.updateSplitters();
37907             this.updateHeaderSortState();
37908         }
37909         this.syncRowHeights();
37910         this.layout();
37911         this.fireEvent("refresh", this);
37912     },
37913
37914     handleColumnMove : function(cm, oldIndex, newIndex){
37915         this.indexMap = null;
37916         var s = this.getScrollState();
37917         this.refresh(true);
37918         this.restoreScroll(s);
37919         this.afterMove(newIndex);
37920     },
37921
37922     afterMove : function(colIndex){
37923         if(this.enableMoveAnim && Roo.enableFx){
37924             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
37925         }
37926         // if multisort - fix sortOrder, and reload..
37927         if (this.grid.dataSource.multiSort) {
37928             // the we can call sort again..
37929             var dm = this.grid.dataSource;
37930             var cm = this.grid.colModel;
37931             var so = [];
37932             for(var i = 0; i < cm.config.length; i++ ) {
37933                 
37934                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
37935                     continue; // dont' bother, it's not in sort list or being set.
37936                 }
37937                 
37938                 so.push(cm.config[i].dataIndex);
37939             };
37940             dm.sortOrder = so;
37941             dm.load(dm.lastOptions);
37942             
37943             
37944         }
37945         
37946     },
37947
37948     updateCell : function(dm, rowIndex, dataIndex){
37949         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
37950         if(typeof colIndex == "undefined"){ // not present in grid
37951             return;
37952         }
37953         var cm = this.grid.colModel;
37954         var cell = this.getCell(rowIndex, colIndex);
37955         var cellText = this.getCellText(rowIndex, colIndex);
37956
37957         var p = {
37958             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
37959             id : cm.getColumnId(colIndex),
37960             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
37961         };
37962         var renderer = cm.getRenderer(colIndex);
37963         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
37964         if(typeof val == "undefined" || val === "") val = "&#160;";
37965         cellText.innerHTML = val;
37966         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
37967         this.syncRowHeights(rowIndex, rowIndex);
37968     },
37969
37970     calcColumnWidth : function(colIndex, maxRowsToMeasure){
37971         var maxWidth = 0;
37972         if(this.grid.autoSizeHeaders){
37973             var h = this.getHeaderCellMeasure(colIndex);
37974             maxWidth = Math.max(maxWidth, h.scrollWidth);
37975         }
37976         var tb, index;
37977         if(this.cm.isLocked(colIndex)){
37978             tb = this.getLockedTable();
37979             index = colIndex;
37980         }else{
37981             tb = this.getBodyTable();
37982             index = colIndex - this.cm.getLockedCount();
37983         }
37984         if(tb && tb.rows){
37985             var rows = tb.rows;
37986             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
37987             for(var i = 0; i < stopIndex; i++){
37988                 var cell = rows[i].childNodes[index].firstChild;
37989                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
37990             }
37991         }
37992         return maxWidth + /*margin for error in IE*/ 5;
37993     },
37994     /**
37995      * Autofit a column to its content.
37996      * @param {Number} colIndex
37997      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
37998      */
37999      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
38000          if(this.cm.isHidden(colIndex)){
38001              return; // can't calc a hidden column
38002          }
38003         if(forceMinSize){
38004             var cid = this.cm.getColumnId(colIndex);
38005             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
38006            if(this.grid.autoSizeHeaders){
38007                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
38008            }
38009         }
38010         var newWidth = this.calcColumnWidth(colIndex);
38011         this.cm.setColumnWidth(colIndex,
38012             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
38013         if(!suppressEvent){
38014             this.grid.fireEvent("columnresize", colIndex, newWidth);
38015         }
38016     },
38017
38018     /**
38019      * Autofits all columns to their content and then expands to fit any extra space in the grid
38020      */
38021      autoSizeColumns : function(){
38022         var cm = this.grid.colModel;
38023         var colCount = cm.getColumnCount();
38024         for(var i = 0; i < colCount; i++){
38025             this.autoSizeColumn(i, true, true);
38026         }
38027         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
38028             this.fitColumns();
38029         }else{
38030             this.updateColumns();
38031             this.layout();
38032         }
38033     },
38034
38035     /**
38036      * Autofits all columns to the grid's width proportionate with their current size
38037      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
38038      */
38039     fitColumns : function(reserveScrollSpace){
38040         var cm = this.grid.colModel;
38041         var colCount = cm.getColumnCount();
38042         var cols = [];
38043         var width = 0;
38044         var i, w;
38045         for (i = 0; i < colCount; i++){
38046             if(!cm.isHidden(i) && !cm.isFixed(i)){
38047                 w = cm.getColumnWidth(i);
38048                 cols.push(i);
38049                 cols.push(w);
38050                 width += w;
38051             }
38052         }
38053         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
38054         if(reserveScrollSpace){
38055             avail -= 17;
38056         }
38057         var frac = (avail - cm.getTotalWidth())/width;
38058         while (cols.length){
38059             w = cols.pop();
38060             i = cols.pop();
38061             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
38062         }
38063         this.updateColumns();
38064         this.layout();
38065     },
38066
38067     onRowSelect : function(rowIndex){
38068         var row = this.getRowComposite(rowIndex);
38069         row.addClass("x-grid-row-selected");
38070     },
38071
38072     onRowDeselect : function(rowIndex){
38073         var row = this.getRowComposite(rowIndex);
38074         row.removeClass("x-grid-row-selected");
38075     },
38076
38077     onCellSelect : function(row, col){
38078         var cell = this.getCell(row, col);
38079         if(cell){
38080             Roo.fly(cell).addClass("x-grid-cell-selected");
38081         }
38082     },
38083
38084     onCellDeselect : function(row, col){
38085         var cell = this.getCell(row, col);
38086         if(cell){
38087             Roo.fly(cell).removeClass("x-grid-cell-selected");
38088         }
38089     },
38090
38091     updateHeaderSortState : function(){
38092         
38093         // sort state can be single { field: xxx, direction : yyy}
38094         // or   { xxx=>ASC , yyy : DESC ..... }
38095         
38096         var mstate = {};
38097         if (!this.ds.multiSort) { 
38098             var state = this.ds.getSortState();
38099             if(!state){
38100                 return;
38101             }
38102             mstate[state.field] = state.direction;
38103             // FIXME... - this is not used here.. but might be elsewhere..
38104             this.sortState = state;
38105             
38106         } else {
38107             mstate = this.ds.sortToggle;
38108         }
38109         //remove existing sort classes..
38110         
38111         var sc = this.sortClasses;
38112         var hds = this.el.select(this.headerSelector).removeClass(sc);
38113         
38114         for(var f in mstate) {
38115         
38116             var sortColumn = this.cm.findColumnIndex(f);
38117             
38118             if(sortColumn != -1){
38119                 var sortDir = mstate[f];        
38120                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
38121             }
38122         }
38123         
38124          
38125         
38126     },
38127
38128
38129     handleHeaderClick : function(g, index,e){
38130         
38131         Roo.log("header click");
38132         
38133         if (Roo.isTouch) {
38134             // touch events on header are handled by context
38135             this.handleHdCtx(g,index,e);
38136             return;
38137         }
38138         
38139         
38140         if(this.headersDisabled){
38141             return;
38142         }
38143         var dm = g.dataSource, cm = g.colModel;
38144         if(!cm.isSortable(index)){
38145             return;
38146         }
38147         g.stopEditing();
38148         
38149         if (dm.multiSort) {
38150             // update the sortOrder
38151             var so = [];
38152             for(var i = 0; i < cm.config.length; i++ ) {
38153                 
38154                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
38155                     continue; // dont' bother, it's not in sort list or being set.
38156                 }
38157                 
38158                 so.push(cm.config[i].dataIndex);
38159             };
38160             dm.sortOrder = so;
38161         }
38162         
38163         
38164         dm.sort(cm.getDataIndex(index));
38165     },
38166
38167
38168     destroy : function(){
38169         if(this.colMenu){
38170             this.colMenu.removeAll();
38171             Roo.menu.MenuMgr.unregister(this.colMenu);
38172             this.colMenu.getEl().remove();
38173             delete this.colMenu;
38174         }
38175         if(this.hmenu){
38176             this.hmenu.removeAll();
38177             Roo.menu.MenuMgr.unregister(this.hmenu);
38178             this.hmenu.getEl().remove();
38179             delete this.hmenu;
38180         }
38181         if(this.grid.enableColumnMove){
38182             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
38183             if(dds){
38184                 for(var dd in dds){
38185                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
38186                         var elid = dds[dd].dragElId;
38187                         dds[dd].unreg();
38188                         Roo.get(elid).remove();
38189                     } else if(dds[dd].config.isTarget){
38190                         dds[dd].proxyTop.remove();
38191                         dds[dd].proxyBottom.remove();
38192                         dds[dd].unreg();
38193                     }
38194                     if(Roo.dd.DDM.locationCache[dd]){
38195                         delete Roo.dd.DDM.locationCache[dd];
38196                     }
38197                 }
38198                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
38199             }
38200         }
38201         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
38202         this.bind(null, null);
38203         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
38204     },
38205
38206     handleLockChange : function(){
38207         this.refresh(true);
38208     },
38209
38210     onDenyColumnLock : function(){
38211
38212     },
38213
38214     onDenyColumnHide : function(){
38215
38216     },
38217
38218     handleHdMenuClick : function(item){
38219         var index = this.hdCtxIndex;
38220         var cm = this.cm, ds = this.ds;
38221         switch(item.id){
38222             case "asc":
38223                 ds.sort(cm.getDataIndex(index), "ASC");
38224                 break;
38225             case "desc":
38226                 ds.sort(cm.getDataIndex(index), "DESC");
38227                 break;
38228             case "lock":
38229                 var lc = cm.getLockedCount();
38230                 if(cm.getColumnCount(true) <= lc+1){
38231                     this.onDenyColumnLock();
38232                     return;
38233                 }
38234                 if(lc != index){
38235                     cm.setLocked(index, true, true);
38236                     cm.moveColumn(index, lc);
38237                     this.grid.fireEvent("columnmove", index, lc);
38238                 }else{
38239                     cm.setLocked(index, true);
38240                 }
38241             break;
38242             case "unlock":
38243                 var lc = cm.getLockedCount();
38244                 if((lc-1) != index){
38245                     cm.setLocked(index, false, true);
38246                     cm.moveColumn(index, lc-1);
38247                     this.grid.fireEvent("columnmove", index, lc-1);
38248                 }else{
38249                     cm.setLocked(index, false);
38250                 }
38251             break;
38252             case 'wider': // used to expand cols on touch..
38253             case 'narrow':
38254                 var cw = cm.getColumnWidth(index);
38255                 cw += (item.id == 'wider' ? 1 : -1) * 50;
38256                 cw = Math.max(0, cw);
38257                 cw = Math.min(cw,4000);
38258                 cm.setColumnWidth(index, cw);
38259                 break;
38260                 
38261             default:
38262                 index = cm.getIndexById(item.id.substr(4));
38263                 if(index != -1){
38264                     if(item.checked && cm.getColumnCount(true) <= 1){
38265                         this.onDenyColumnHide();
38266                         return false;
38267                     }
38268                     cm.setHidden(index, item.checked);
38269                 }
38270         }
38271         return true;
38272     },
38273
38274     beforeColMenuShow : function(){
38275         var cm = this.cm,  colCount = cm.getColumnCount();
38276         this.colMenu.removeAll();
38277         for(var i = 0; i < colCount; i++){
38278             this.colMenu.add(new Roo.menu.CheckItem({
38279                 id: "col-"+cm.getColumnId(i),
38280                 text: cm.getColumnHeader(i),
38281                 checked: !cm.isHidden(i),
38282                 hideOnClick:false
38283             }));
38284         }
38285     },
38286
38287     handleHdCtx : function(g, index, e){
38288         e.stopEvent();
38289         var hd = this.getHeaderCell(index);
38290         this.hdCtxIndex = index;
38291         var ms = this.hmenu.items, cm = this.cm;
38292         ms.get("asc").setDisabled(!cm.isSortable(index));
38293         ms.get("desc").setDisabled(!cm.isSortable(index));
38294         if(this.grid.enableColLock !== false){
38295             ms.get("lock").setDisabled(cm.isLocked(index));
38296             ms.get("unlock").setDisabled(!cm.isLocked(index));
38297         }
38298         this.hmenu.show(hd, "tl-bl");
38299     },
38300
38301     handleHdOver : function(e){
38302         var hd = this.findHeaderCell(e.getTarget());
38303         if(hd && !this.headersDisabled){
38304             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
38305                this.fly(hd).addClass("x-grid-hd-over");
38306             }
38307         }
38308     },
38309
38310     handleHdOut : function(e){
38311         var hd = this.findHeaderCell(e.getTarget());
38312         if(hd){
38313             this.fly(hd).removeClass("x-grid-hd-over");
38314         }
38315     },
38316
38317     handleSplitDblClick : function(e, t){
38318         var i = this.getCellIndex(t);
38319         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
38320             this.autoSizeColumn(i, true);
38321             this.layout();
38322         }
38323     },
38324
38325     render : function(){
38326
38327         var cm = this.cm;
38328         var colCount = cm.getColumnCount();
38329
38330         if(this.grid.monitorWindowResize === true){
38331             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38332         }
38333         var header = this.renderHeaders();
38334         var body = this.templates.body.apply({rows:""});
38335         var html = this.templates.master.apply({
38336             lockedBody: body,
38337             body: body,
38338             lockedHeader: header[0],
38339             header: header[1]
38340         });
38341
38342         //this.updateColumns();
38343
38344         this.grid.getGridEl().dom.innerHTML = html;
38345
38346         this.initElements();
38347         
38348         // a kludge to fix the random scolling effect in webkit
38349         this.el.on("scroll", function() {
38350             this.el.dom.scrollTop=0; // hopefully not recursive..
38351         },this);
38352
38353         this.scroller.on("scroll", this.handleScroll, this);
38354         this.lockedBody.on("mousewheel", this.handleWheel, this);
38355         this.mainBody.on("mousewheel", this.handleWheel, this);
38356
38357         this.mainHd.on("mouseover", this.handleHdOver, this);
38358         this.mainHd.on("mouseout", this.handleHdOut, this);
38359         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
38360                 {delegate: "."+this.splitClass});
38361
38362         this.lockedHd.on("mouseover", this.handleHdOver, this);
38363         this.lockedHd.on("mouseout", this.handleHdOut, this);
38364         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
38365                 {delegate: "."+this.splitClass});
38366
38367         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
38368             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38369         }
38370
38371         this.updateSplitters();
38372
38373         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
38374             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38375             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38376         }
38377
38378         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
38379             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
38380             this.hmenu.add(
38381                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
38382                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
38383             );
38384             if(this.grid.enableColLock !== false){
38385                 this.hmenu.add('-',
38386                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
38387                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
38388                 );
38389             }
38390             if (Roo.isTouch) {
38391                  this.hmenu.add('-',
38392                     {id:"wider", text: this.columnsWiderText},
38393                     {id:"narrow", text: this.columnsNarrowText }
38394                 );
38395                 
38396                  
38397             }
38398             
38399             if(this.grid.enableColumnHide !== false){
38400
38401                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
38402                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
38403                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
38404
38405                 this.hmenu.add('-',
38406                     {id:"columns", text: this.columnsText, menu: this.colMenu}
38407                 );
38408             }
38409             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
38410
38411             this.grid.on("headercontextmenu", this.handleHdCtx, this);
38412         }
38413
38414         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
38415             this.dd = new Roo.grid.GridDragZone(this.grid, {
38416                 ddGroup : this.grid.ddGroup || 'GridDD'
38417             });
38418             
38419         }
38420
38421         /*
38422         for(var i = 0; i < colCount; i++){
38423             if(cm.isHidden(i)){
38424                 this.hideColumn(i);
38425             }
38426             if(cm.config[i].align){
38427                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
38428                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
38429             }
38430         }*/
38431         
38432         this.updateHeaderSortState();
38433
38434         this.beforeInitialResize();
38435         this.layout(true);
38436
38437         // two part rendering gives faster view to the user
38438         this.renderPhase2.defer(1, this);
38439     },
38440
38441     renderPhase2 : function(){
38442         // render the rows now
38443         this.refresh();
38444         if(this.grid.autoSizeColumns){
38445             this.autoSizeColumns();
38446         }
38447     },
38448
38449     beforeInitialResize : function(){
38450
38451     },
38452
38453     onColumnSplitterMoved : function(i, w){
38454         this.userResized = true;
38455         var cm = this.grid.colModel;
38456         cm.setColumnWidth(i, w, true);
38457         var cid = cm.getColumnId(i);
38458         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
38459         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
38460         this.updateSplitters();
38461         this.layout();
38462         this.grid.fireEvent("columnresize", i, w);
38463     },
38464
38465     syncRowHeights : function(startIndex, endIndex){
38466         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
38467             startIndex = startIndex || 0;
38468             var mrows = this.getBodyTable().rows;
38469             var lrows = this.getLockedTable().rows;
38470             var len = mrows.length-1;
38471             endIndex = Math.min(endIndex || len, len);
38472             for(var i = startIndex; i <= endIndex; i++){
38473                 var m = mrows[i], l = lrows[i];
38474                 var h = Math.max(m.offsetHeight, l.offsetHeight);
38475                 m.style.height = l.style.height = h + "px";
38476             }
38477         }
38478     },
38479
38480     layout : function(initialRender, is2ndPass){
38481         var g = this.grid;
38482         var auto = g.autoHeight;
38483         var scrollOffset = 16;
38484         var c = g.getGridEl(), cm = this.cm,
38485                 expandCol = g.autoExpandColumn,
38486                 gv = this;
38487         //c.beginMeasure();
38488
38489         if(!c.dom.offsetWidth){ // display:none?
38490             if(initialRender){
38491                 this.lockedWrap.show();
38492                 this.mainWrap.show();
38493             }
38494             return;
38495         }
38496
38497         var hasLock = this.cm.isLocked(0);
38498
38499         var tbh = this.headerPanel.getHeight();
38500         var bbh = this.footerPanel.getHeight();
38501
38502         if(auto){
38503             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
38504             var newHeight = ch + c.getBorderWidth("tb");
38505             if(g.maxHeight){
38506                 newHeight = Math.min(g.maxHeight, newHeight);
38507             }
38508             c.setHeight(newHeight);
38509         }
38510
38511         if(g.autoWidth){
38512             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
38513         }
38514
38515         var s = this.scroller;
38516
38517         var csize = c.getSize(true);
38518
38519         this.el.setSize(csize.width, csize.height);
38520
38521         this.headerPanel.setWidth(csize.width);
38522         this.footerPanel.setWidth(csize.width);
38523
38524         var hdHeight = this.mainHd.getHeight();
38525         var vw = csize.width;
38526         var vh = csize.height - (tbh + bbh);
38527
38528         s.setSize(vw, vh);
38529
38530         var bt = this.getBodyTable();
38531         var ltWidth = hasLock ?
38532                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
38533
38534         var scrollHeight = bt.offsetHeight;
38535         var scrollWidth = ltWidth + bt.offsetWidth;
38536         var vscroll = false, hscroll = false;
38537
38538         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
38539
38540         var lw = this.lockedWrap, mw = this.mainWrap;
38541         var lb = this.lockedBody, mb = this.mainBody;
38542
38543         setTimeout(function(){
38544             var t = s.dom.offsetTop;
38545             var w = s.dom.clientWidth,
38546                 h = s.dom.clientHeight;
38547
38548             lw.setTop(t);
38549             lw.setSize(ltWidth, h);
38550
38551             mw.setLeftTop(ltWidth, t);
38552             mw.setSize(w-ltWidth, h);
38553
38554             lb.setHeight(h-hdHeight);
38555             mb.setHeight(h-hdHeight);
38556
38557             if(is2ndPass !== true && !gv.userResized && expandCol){
38558                 // high speed resize without full column calculation
38559                 
38560                 var ci = cm.getIndexById(expandCol);
38561                 if (ci < 0) {
38562                     ci = cm.findColumnIndex(expandCol);
38563                 }
38564                 ci = Math.max(0, ci); // make sure it's got at least the first col.
38565                 var expandId = cm.getColumnId(ci);
38566                 var  tw = cm.getTotalWidth(false);
38567                 var currentWidth = cm.getColumnWidth(ci);
38568                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
38569                 if(currentWidth != cw){
38570                     cm.setColumnWidth(ci, cw, true);
38571                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38572                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38573                     gv.updateSplitters();
38574                     gv.layout(false, true);
38575                 }
38576             }
38577
38578             if(initialRender){
38579                 lw.show();
38580                 mw.show();
38581             }
38582             //c.endMeasure();
38583         }, 10);
38584     },
38585
38586     onWindowResize : function(){
38587         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
38588             return;
38589         }
38590         this.layout();
38591     },
38592
38593     appendFooter : function(parentEl){
38594         return null;
38595     },
38596
38597     sortAscText : "Sort Ascending",
38598     sortDescText : "Sort Descending",
38599     lockText : "Lock Column",
38600     unlockText : "Unlock Column",
38601     columnsText : "Columns",
38602  
38603     columnsWiderText : "Wider",
38604     columnsNarrowText : "Thinner"
38605 });
38606
38607
38608 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
38609     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
38610     this.proxy.el.addClass('x-grid3-col-dd');
38611 };
38612
38613 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
38614     handleMouseDown : function(e){
38615
38616     },
38617
38618     callHandleMouseDown : function(e){
38619         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
38620     }
38621 });
38622 /*
38623  * Based on:
38624  * Ext JS Library 1.1.1
38625  * Copyright(c) 2006-2007, Ext JS, LLC.
38626  *
38627  * Originally Released Under LGPL - original licence link has changed is not relivant.
38628  *
38629  * Fork - LGPL
38630  * <script type="text/javascript">
38631  */
38632  
38633 // private
38634 // This is a support class used internally by the Grid components
38635 Roo.grid.SplitDragZone = function(grid, hd, hd2){
38636     this.grid = grid;
38637     this.view = grid.getView();
38638     this.proxy = this.view.resizeProxy;
38639     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
38640         "gridSplitters" + this.grid.getGridEl().id, {
38641         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
38642     });
38643     this.setHandleElId(Roo.id(hd));
38644     this.setOuterHandleElId(Roo.id(hd2));
38645     this.scroll = false;
38646 };
38647 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
38648     fly: Roo.Element.fly,
38649
38650     b4StartDrag : function(x, y){
38651         this.view.headersDisabled = true;
38652         this.proxy.setHeight(this.view.mainWrap.getHeight());
38653         var w = this.cm.getColumnWidth(this.cellIndex);
38654         var minw = Math.max(w-this.grid.minColumnWidth, 0);
38655         this.resetConstraints();
38656         this.setXConstraint(minw, 1000);
38657         this.setYConstraint(0, 0);
38658         this.minX = x - minw;
38659         this.maxX = x + 1000;
38660         this.startPos = x;
38661         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
38662     },
38663
38664
38665     handleMouseDown : function(e){
38666         ev = Roo.EventObject.setEvent(e);
38667         var t = this.fly(ev.getTarget());
38668         if(t.hasClass("x-grid-split")){
38669             this.cellIndex = this.view.getCellIndex(t.dom);
38670             this.split = t.dom;
38671             this.cm = this.grid.colModel;
38672             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
38673                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
38674             }
38675         }
38676     },
38677
38678     endDrag : function(e){
38679         this.view.headersDisabled = false;
38680         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
38681         var diff = endX - this.startPos;
38682         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
38683     },
38684
38685     autoOffset : function(){
38686         this.setDelta(0,0);
38687     }
38688 });/*
38689  * Based on:
38690  * Ext JS Library 1.1.1
38691  * Copyright(c) 2006-2007, Ext JS, LLC.
38692  *
38693  * Originally Released Under LGPL - original licence link has changed is not relivant.
38694  *
38695  * Fork - LGPL
38696  * <script type="text/javascript">
38697  */
38698  
38699 // private
38700 // This is a support class used internally by the Grid components
38701 Roo.grid.GridDragZone = function(grid, config){
38702     this.view = grid.getView();
38703     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
38704     if(this.view.lockedBody){
38705         this.setHandleElId(Roo.id(this.view.mainBody.dom));
38706         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
38707     }
38708     this.scroll = false;
38709     this.grid = grid;
38710     this.ddel = document.createElement('div');
38711     this.ddel.className = 'x-grid-dd-wrap';
38712 };
38713
38714 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
38715     ddGroup : "GridDD",
38716
38717     getDragData : function(e){
38718         var t = Roo.lib.Event.getTarget(e);
38719         var rowIndex = this.view.findRowIndex(t);
38720         var sm = this.grid.selModel;
38721             
38722         //Roo.log(rowIndex);
38723         
38724         if (sm.getSelectedCell) {
38725             // cell selection..
38726             if (!sm.getSelectedCell()) {
38727                 return false;
38728             }
38729             if (rowIndex != sm.getSelectedCell()[0]) {
38730                 return false;
38731             }
38732         
38733         }
38734         
38735         if(rowIndex !== false){
38736             
38737             // if editorgrid.. 
38738             
38739             
38740             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
38741                
38742             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
38743               //  
38744             //}
38745             if (e.hasModifier()){
38746                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
38747             }
38748             
38749             Roo.log("getDragData");
38750             
38751             return {
38752                 grid: this.grid,
38753                 ddel: this.ddel,
38754                 rowIndex: rowIndex,
38755                 selections:sm.getSelections ? sm.getSelections() : (
38756                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
38757                 )
38758             };
38759         }
38760         return false;
38761     },
38762
38763     onInitDrag : function(e){
38764         var data = this.dragData;
38765         this.ddel.innerHTML = this.grid.getDragDropText();
38766         this.proxy.update(this.ddel);
38767         // fire start drag?
38768     },
38769
38770     afterRepair : function(){
38771         this.dragging = false;
38772     },
38773
38774     getRepairXY : function(e, data){
38775         return false;
38776     },
38777
38778     onEndDrag : function(data, e){
38779         // fire end drag?
38780     },
38781
38782     onValidDrop : function(dd, e, id){
38783         // fire drag drop?
38784         this.hideProxy();
38785     },
38786
38787     beforeInvalidDrop : function(e, id){
38788
38789     }
38790 });/*
38791  * Based on:
38792  * Ext JS Library 1.1.1
38793  * Copyright(c) 2006-2007, Ext JS, LLC.
38794  *
38795  * Originally Released Under LGPL - original licence link has changed is not relivant.
38796  *
38797  * Fork - LGPL
38798  * <script type="text/javascript">
38799  */
38800  
38801
38802 /**
38803  * @class Roo.grid.ColumnModel
38804  * @extends Roo.util.Observable
38805  * This is the default implementation of a ColumnModel used by the Grid. It defines
38806  * the columns in the grid.
38807  * <br>Usage:<br>
38808  <pre><code>
38809  var colModel = new Roo.grid.ColumnModel([
38810         {header: "Ticker", width: 60, sortable: true, locked: true},
38811         {header: "Company Name", width: 150, sortable: true},
38812         {header: "Market Cap.", width: 100, sortable: true},
38813         {header: "$ Sales", width: 100, sortable: true, renderer: money},
38814         {header: "Employees", width: 100, sortable: true, resizable: false}
38815  ]);
38816  </code></pre>
38817  * <p>
38818  
38819  * The config options listed for this class are options which may appear in each
38820  * individual column definition.
38821  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
38822  * @constructor
38823  * @param {Object} config An Array of column config objects. See this class's
38824  * config objects for details.
38825 */
38826 Roo.grid.ColumnModel = function(config){
38827         /**
38828      * The config passed into the constructor
38829      */
38830     this.config = config;
38831     this.lookup = {};
38832
38833     // if no id, create one
38834     // if the column does not have a dataIndex mapping,
38835     // map it to the order it is in the config
38836     for(var i = 0, len = config.length; i < len; i++){
38837         var c = config[i];
38838         if(typeof c.dataIndex == "undefined"){
38839             c.dataIndex = i;
38840         }
38841         if(typeof c.renderer == "string"){
38842             c.renderer = Roo.util.Format[c.renderer];
38843         }
38844         if(typeof c.id == "undefined"){
38845             c.id = Roo.id();
38846         }
38847         if(c.editor && c.editor.xtype){
38848             c.editor  = Roo.factory(c.editor, Roo.grid);
38849         }
38850         if(c.editor && c.editor.isFormField){
38851             c.editor = new Roo.grid.GridEditor(c.editor);
38852         }
38853         this.lookup[c.id] = c;
38854     }
38855
38856     /**
38857      * The width of columns which have no width specified (defaults to 100)
38858      * @type Number
38859      */
38860     this.defaultWidth = 100;
38861
38862     /**
38863      * Default sortable of columns which have no sortable specified (defaults to false)
38864      * @type Boolean
38865      */
38866     this.defaultSortable = false;
38867
38868     this.addEvents({
38869         /**
38870              * @event widthchange
38871              * Fires when the width of a column changes.
38872              * @param {ColumnModel} this
38873              * @param {Number} columnIndex The column index
38874              * @param {Number} newWidth The new width
38875              */
38876             "widthchange": true,
38877         /**
38878              * @event headerchange
38879              * Fires when the text of a header changes.
38880              * @param {ColumnModel} this
38881              * @param {Number} columnIndex The column index
38882              * @param {Number} newText The new header text
38883              */
38884             "headerchange": true,
38885         /**
38886              * @event hiddenchange
38887              * Fires when a column is hidden or "unhidden".
38888              * @param {ColumnModel} this
38889              * @param {Number} columnIndex The column index
38890              * @param {Boolean} hidden true if hidden, false otherwise
38891              */
38892             "hiddenchange": true,
38893             /**
38894          * @event columnmoved
38895          * Fires when a column is moved.
38896          * @param {ColumnModel} this
38897          * @param {Number} oldIndex
38898          * @param {Number} newIndex
38899          */
38900         "columnmoved" : true,
38901         /**
38902          * @event columlockchange
38903          * Fires when a column's locked state is changed
38904          * @param {ColumnModel} this
38905          * @param {Number} colIndex
38906          * @param {Boolean} locked true if locked
38907          */
38908         "columnlockchange" : true
38909     });
38910     Roo.grid.ColumnModel.superclass.constructor.call(this);
38911 };
38912 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
38913     /**
38914      * @cfg {String} header The header text to display in the Grid view.
38915      */
38916     /**
38917      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
38918      * {@link Roo.data.Record} definition from which to draw the column's value. If not
38919      * specified, the column's index is used as an index into the Record's data Array.
38920      */
38921     /**
38922      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
38923      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
38924      */
38925     /**
38926      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
38927      * Defaults to the value of the {@link #defaultSortable} property.
38928      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
38929      */
38930     /**
38931      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
38932      */
38933     /**
38934      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
38935      */
38936     /**
38937      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
38938      */
38939     /**
38940      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
38941      */
38942     /**
38943      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
38944      * given the cell's data value. See {@link #setRenderer}. If not specified, the
38945      * default renderer uses the raw data value. If an object is returned (bootstrap only)
38946      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
38947      */
38948        /**
38949      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
38950      */
38951     /**
38952      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
38953      */
38954     /**
38955      * @cfg {String} cursor (Optional)
38956      */
38957     /**
38958      * @cfg {String} tooltip (Optional)
38959      */
38960     /**
38961      * @cfg {Number} xs (Optional)
38962      */
38963     /**
38964      * @cfg {Number} sm (Optional)
38965      */
38966     /**
38967      * @cfg {Number} md (Optional)
38968      */
38969     /**
38970      * @cfg {Number} lg (Optional)
38971      */
38972     /**
38973      * Returns the id of the column at the specified index.
38974      * @param {Number} index The column index
38975      * @return {String} the id
38976      */
38977     getColumnId : function(index){
38978         return this.config[index].id;
38979     },
38980
38981     /**
38982      * Returns the column for a specified id.
38983      * @param {String} id The column id
38984      * @return {Object} the column
38985      */
38986     getColumnById : function(id){
38987         return this.lookup[id];
38988     },
38989
38990     
38991     /**
38992      * Returns the column for a specified dataIndex.
38993      * @param {String} dataIndex The column dataIndex
38994      * @return {Object|Boolean} the column or false if not found
38995      */
38996     getColumnByDataIndex: function(dataIndex){
38997         var index = this.findColumnIndex(dataIndex);
38998         return index > -1 ? this.config[index] : false;
38999     },
39000     
39001     /**
39002      * Returns the index for a specified column id.
39003      * @param {String} id The column id
39004      * @return {Number} the index, or -1 if not found
39005      */
39006     getIndexById : function(id){
39007         for(var i = 0, len = this.config.length; i < len; i++){
39008             if(this.config[i].id == id){
39009                 return i;
39010             }
39011         }
39012         return -1;
39013     },
39014     
39015     /**
39016      * Returns the index for a specified column dataIndex.
39017      * @param {String} dataIndex The column dataIndex
39018      * @return {Number} the index, or -1 if not found
39019      */
39020     
39021     findColumnIndex : function(dataIndex){
39022         for(var i = 0, len = this.config.length; i < len; i++){
39023             if(this.config[i].dataIndex == dataIndex){
39024                 return i;
39025             }
39026         }
39027         return -1;
39028     },
39029     
39030     
39031     moveColumn : function(oldIndex, newIndex){
39032         var c = this.config[oldIndex];
39033         this.config.splice(oldIndex, 1);
39034         this.config.splice(newIndex, 0, c);
39035         this.dataMap = null;
39036         this.fireEvent("columnmoved", this, oldIndex, newIndex);
39037     },
39038
39039     isLocked : function(colIndex){
39040         return this.config[colIndex].locked === true;
39041     },
39042
39043     setLocked : function(colIndex, value, suppressEvent){
39044         if(this.isLocked(colIndex) == value){
39045             return;
39046         }
39047         this.config[colIndex].locked = value;
39048         if(!suppressEvent){
39049             this.fireEvent("columnlockchange", this, colIndex, value);
39050         }
39051     },
39052
39053     getTotalLockedWidth : function(){
39054         var totalWidth = 0;
39055         for(var i = 0; i < this.config.length; i++){
39056             if(this.isLocked(i) && !this.isHidden(i)){
39057                 this.totalWidth += this.getColumnWidth(i);
39058             }
39059         }
39060         return totalWidth;
39061     },
39062
39063     getLockedCount : function(){
39064         for(var i = 0, len = this.config.length; i < len; i++){
39065             if(!this.isLocked(i)){
39066                 return i;
39067             }
39068         }
39069     },
39070
39071     /**
39072      * Returns the number of columns.
39073      * @return {Number}
39074      */
39075     getColumnCount : function(visibleOnly){
39076         if(visibleOnly === true){
39077             var c = 0;
39078             for(var i = 0, len = this.config.length; i < len; i++){
39079                 if(!this.isHidden(i)){
39080                     c++;
39081                 }
39082             }
39083             return c;
39084         }
39085         return this.config.length;
39086     },
39087
39088     /**
39089      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
39090      * @param {Function} fn
39091      * @param {Object} scope (optional)
39092      * @return {Array} result
39093      */
39094     getColumnsBy : function(fn, scope){
39095         var r = [];
39096         for(var i = 0, len = this.config.length; i < len; i++){
39097             var c = this.config[i];
39098             if(fn.call(scope||this, c, i) === true){
39099                 r[r.length] = c;
39100             }
39101         }
39102         return r;
39103     },
39104
39105     /**
39106      * Returns true if the specified column is sortable.
39107      * @param {Number} col The column index
39108      * @return {Boolean}
39109      */
39110     isSortable : function(col){
39111         if(typeof this.config[col].sortable == "undefined"){
39112             return this.defaultSortable;
39113         }
39114         return this.config[col].sortable;
39115     },
39116
39117     /**
39118      * Returns the rendering (formatting) function defined for the column.
39119      * @param {Number} col The column index.
39120      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
39121      */
39122     getRenderer : function(col){
39123         if(!this.config[col].renderer){
39124             return Roo.grid.ColumnModel.defaultRenderer;
39125         }
39126         return this.config[col].renderer;
39127     },
39128
39129     /**
39130      * Sets the rendering (formatting) function for a column.
39131      * @param {Number} col The column index
39132      * @param {Function} fn The function to use to process the cell's raw data
39133      * to return HTML markup for the grid view. The render function is called with
39134      * the following parameters:<ul>
39135      * <li>Data value.</li>
39136      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
39137      * <li>css A CSS style string to apply to the table cell.</li>
39138      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
39139      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
39140      * <li>Row index</li>
39141      * <li>Column index</li>
39142      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
39143      */
39144     setRenderer : function(col, fn){
39145         this.config[col].renderer = fn;
39146     },
39147
39148     /**
39149      * Returns the width for the specified column.
39150      * @param {Number} col The column index
39151      * @return {Number}
39152      */
39153     getColumnWidth : function(col){
39154         return this.config[col].width * 1 || this.defaultWidth;
39155     },
39156
39157     /**
39158      * Sets the width for a column.
39159      * @param {Number} col The column index
39160      * @param {Number} width The new width
39161      */
39162     setColumnWidth : function(col, width, suppressEvent){
39163         this.config[col].width = width;
39164         this.totalWidth = null;
39165         if(!suppressEvent){
39166              this.fireEvent("widthchange", this, col, width);
39167         }
39168     },
39169
39170     /**
39171      * Returns the total width of all columns.
39172      * @param {Boolean} includeHidden True to include hidden column widths
39173      * @return {Number}
39174      */
39175     getTotalWidth : function(includeHidden){
39176         if(!this.totalWidth){
39177             this.totalWidth = 0;
39178             for(var i = 0, len = this.config.length; i < len; i++){
39179                 if(includeHidden || !this.isHidden(i)){
39180                     this.totalWidth += this.getColumnWidth(i);
39181                 }
39182             }
39183         }
39184         return this.totalWidth;
39185     },
39186
39187     /**
39188      * Returns the header for the specified column.
39189      * @param {Number} col The column index
39190      * @return {String}
39191      */
39192     getColumnHeader : function(col){
39193         return this.config[col].header;
39194     },
39195
39196     /**
39197      * Sets the header for a column.
39198      * @param {Number} col The column index
39199      * @param {String} header The new header
39200      */
39201     setColumnHeader : function(col, header){
39202         this.config[col].header = header;
39203         this.fireEvent("headerchange", this, col, header);
39204     },
39205
39206     /**
39207      * Returns the tooltip for the specified column.
39208      * @param {Number} col The column index
39209      * @return {String}
39210      */
39211     getColumnTooltip : function(col){
39212             return this.config[col].tooltip;
39213     },
39214     /**
39215      * Sets the tooltip for a column.
39216      * @param {Number} col The column index
39217      * @param {String} tooltip The new tooltip
39218      */
39219     setColumnTooltip : function(col, tooltip){
39220             this.config[col].tooltip = tooltip;
39221     },
39222
39223     /**
39224      * Returns the dataIndex for the specified column.
39225      * @param {Number} col The column index
39226      * @return {Number}
39227      */
39228     getDataIndex : function(col){
39229         return this.config[col].dataIndex;
39230     },
39231
39232     /**
39233      * Sets the dataIndex for a column.
39234      * @param {Number} col The column index
39235      * @param {Number} dataIndex The new dataIndex
39236      */
39237     setDataIndex : function(col, dataIndex){
39238         this.config[col].dataIndex = dataIndex;
39239     },
39240
39241     
39242     
39243     /**
39244      * Returns true if the cell is editable.
39245      * @param {Number} colIndex The column index
39246      * @param {Number} rowIndex The row index
39247      * @return {Boolean}
39248      */
39249     isCellEditable : function(colIndex, rowIndex){
39250         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
39251     },
39252
39253     /**
39254      * Returns the editor defined for the cell/column.
39255      * return false or null to disable editing.
39256      * @param {Number} colIndex The column index
39257      * @param {Number} rowIndex The row index
39258      * @return {Object}
39259      */
39260     getCellEditor : function(colIndex, rowIndex){
39261         return this.config[colIndex].editor;
39262     },
39263
39264     /**
39265      * Sets if a column is editable.
39266      * @param {Number} col The column index
39267      * @param {Boolean} editable True if the column is editable
39268      */
39269     setEditable : function(col, editable){
39270         this.config[col].editable = editable;
39271     },
39272
39273
39274     /**
39275      * Returns true if the column is hidden.
39276      * @param {Number} colIndex The column index
39277      * @return {Boolean}
39278      */
39279     isHidden : function(colIndex){
39280         return this.config[colIndex].hidden;
39281     },
39282
39283
39284     /**
39285      * Returns true if the column width cannot be changed
39286      */
39287     isFixed : function(colIndex){
39288         return this.config[colIndex].fixed;
39289     },
39290
39291     /**
39292      * Returns true if the column can be resized
39293      * @return {Boolean}
39294      */
39295     isResizable : function(colIndex){
39296         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
39297     },
39298     /**
39299      * Sets if a column is hidden.
39300      * @param {Number} colIndex The column index
39301      * @param {Boolean} hidden True if the column is hidden
39302      */
39303     setHidden : function(colIndex, hidden){
39304         this.config[colIndex].hidden = hidden;
39305         this.totalWidth = null;
39306         this.fireEvent("hiddenchange", this, colIndex, hidden);
39307     },
39308
39309     /**
39310      * Sets the editor for a column.
39311      * @param {Number} col The column index
39312      * @param {Object} editor The editor object
39313      */
39314     setEditor : function(col, editor){
39315         this.config[col].editor = editor;
39316     }
39317 });
39318
39319 Roo.grid.ColumnModel.defaultRenderer = function(value){
39320         if(typeof value == "string" && value.length < 1){
39321             return "&#160;";
39322         }
39323         return value;
39324 };
39325
39326 // Alias for backwards compatibility
39327 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
39328 /*
39329  * Based on:
39330  * Ext JS Library 1.1.1
39331  * Copyright(c) 2006-2007, Ext JS, LLC.
39332  *
39333  * Originally Released Under LGPL - original licence link has changed is not relivant.
39334  *
39335  * Fork - LGPL
39336  * <script type="text/javascript">
39337  */
39338
39339 /**
39340  * @class Roo.grid.AbstractSelectionModel
39341  * @extends Roo.util.Observable
39342  * Abstract base class for grid SelectionModels.  It provides the interface that should be
39343  * implemented by descendant classes.  This class should not be directly instantiated.
39344  * @constructor
39345  */
39346 Roo.grid.AbstractSelectionModel = function(){
39347     this.locked = false;
39348     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
39349 };
39350
39351 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
39352     /** @ignore Called by the grid automatically. Do not call directly. */
39353     init : function(grid){
39354         this.grid = grid;
39355         this.initEvents();
39356     },
39357
39358     /**
39359      * Locks the selections.
39360      */
39361     lock : function(){
39362         this.locked = true;
39363     },
39364
39365     /**
39366      * Unlocks the selections.
39367      */
39368     unlock : function(){
39369         this.locked = false;
39370     },
39371
39372     /**
39373      * Returns true if the selections are locked.
39374      * @return {Boolean}
39375      */
39376     isLocked : function(){
39377         return this.locked;
39378     }
39379 });/*
39380  * Based on:
39381  * Ext JS Library 1.1.1
39382  * Copyright(c) 2006-2007, Ext JS, LLC.
39383  *
39384  * Originally Released Under LGPL - original licence link has changed is not relivant.
39385  *
39386  * Fork - LGPL
39387  * <script type="text/javascript">
39388  */
39389 /**
39390  * @extends Roo.grid.AbstractSelectionModel
39391  * @class Roo.grid.RowSelectionModel
39392  * The default SelectionModel used by {@link Roo.grid.Grid}.
39393  * It supports multiple selections and keyboard selection/navigation. 
39394  * @constructor
39395  * @param {Object} config
39396  */
39397 Roo.grid.RowSelectionModel = function(config){
39398     Roo.apply(this, config);
39399     this.selections = new Roo.util.MixedCollection(false, function(o){
39400         return o.id;
39401     });
39402
39403     this.last = false;
39404     this.lastActive = false;
39405
39406     this.addEvents({
39407         /**
39408              * @event selectionchange
39409              * Fires when the selection changes
39410              * @param {SelectionModel} this
39411              */
39412             "selectionchange" : true,
39413         /**
39414              * @event afterselectionchange
39415              * Fires after the selection changes (eg. by key press or clicking)
39416              * @param {SelectionModel} this
39417              */
39418             "afterselectionchange" : true,
39419         /**
39420              * @event beforerowselect
39421              * Fires when a row is selected being selected, return false to cancel.
39422              * @param {SelectionModel} this
39423              * @param {Number} rowIndex The selected index
39424              * @param {Boolean} keepExisting False if other selections will be cleared
39425              */
39426             "beforerowselect" : true,
39427         /**
39428              * @event rowselect
39429              * Fires when a row is selected.
39430              * @param {SelectionModel} this
39431              * @param {Number} rowIndex The selected index
39432              * @param {Roo.data.Record} r The record
39433              */
39434             "rowselect" : true,
39435         /**
39436              * @event rowdeselect
39437              * Fires when a row is deselected.
39438              * @param {SelectionModel} this
39439              * @param {Number} rowIndex The selected index
39440              */
39441         "rowdeselect" : true
39442     });
39443     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
39444     this.locked = false;
39445 };
39446
39447 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
39448     /**
39449      * @cfg {Boolean} singleSelect
39450      * True to allow selection of only one row at a time (defaults to false)
39451      */
39452     singleSelect : false,
39453
39454     // private
39455     initEvents : function(){
39456
39457         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
39458             this.grid.on("mousedown", this.handleMouseDown, this);
39459         }else{ // allow click to work like normal
39460             this.grid.on("rowclick", this.handleDragableRowClick, this);
39461         }
39462
39463         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
39464             "up" : function(e){
39465                 if(!e.shiftKey){
39466                     this.selectPrevious(e.shiftKey);
39467                 }else if(this.last !== false && this.lastActive !== false){
39468                     var last = this.last;
39469                     this.selectRange(this.last,  this.lastActive-1);
39470                     this.grid.getView().focusRow(this.lastActive);
39471                     if(last !== false){
39472                         this.last = last;
39473                     }
39474                 }else{
39475                     this.selectFirstRow();
39476                 }
39477                 this.fireEvent("afterselectionchange", this);
39478             },
39479             "down" : function(e){
39480                 if(!e.shiftKey){
39481                     this.selectNext(e.shiftKey);
39482                 }else if(this.last !== false && this.lastActive !== false){
39483                     var last = this.last;
39484                     this.selectRange(this.last,  this.lastActive+1);
39485                     this.grid.getView().focusRow(this.lastActive);
39486                     if(last !== false){
39487                         this.last = last;
39488                     }
39489                 }else{
39490                     this.selectFirstRow();
39491                 }
39492                 this.fireEvent("afterselectionchange", this);
39493             },
39494             scope: this
39495         });
39496
39497         var view = this.grid.view;
39498         view.on("refresh", this.onRefresh, this);
39499         view.on("rowupdated", this.onRowUpdated, this);
39500         view.on("rowremoved", this.onRemove, this);
39501     },
39502
39503     // private
39504     onRefresh : function(){
39505         var ds = this.grid.dataSource, i, v = this.grid.view;
39506         var s = this.selections;
39507         s.each(function(r){
39508             if((i = ds.indexOfId(r.id)) != -1){
39509                 v.onRowSelect(i);
39510                 s.add(ds.getAt(i)); // updating the selection relate data
39511             }else{
39512                 s.remove(r);
39513             }
39514         });
39515     },
39516
39517     // private
39518     onRemove : function(v, index, r){
39519         this.selections.remove(r);
39520     },
39521
39522     // private
39523     onRowUpdated : function(v, index, r){
39524         if(this.isSelected(r)){
39525             v.onRowSelect(index);
39526         }
39527     },
39528
39529     /**
39530      * Select records.
39531      * @param {Array} records The records to select
39532      * @param {Boolean} keepExisting (optional) True to keep existing selections
39533      */
39534     selectRecords : function(records, keepExisting){
39535         if(!keepExisting){
39536             this.clearSelections();
39537         }
39538         var ds = this.grid.dataSource;
39539         for(var i = 0, len = records.length; i < len; i++){
39540             this.selectRow(ds.indexOf(records[i]), true);
39541         }
39542     },
39543
39544     /**
39545      * Gets the number of selected rows.
39546      * @return {Number}
39547      */
39548     getCount : function(){
39549         return this.selections.length;
39550     },
39551
39552     /**
39553      * Selects the first row in the grid.
39554      */
39555     selectFirstRow : function(){
39556         this.selectRow(0);
39557     },
39558
39559     /**
39560      * Select the last row.
39561      * @param {Boolean} keepExisting (optional) True to keep existing selections
39562      */
39563     selectLastRow : function(keepExisting){
39564         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
39565     },
39566
39567     /**
39568      * Selects the row immediately following the last selected row.
39569      * @param {Boolean} keepExisting (optional) True to keep existing selections
39570      */
39571     selectNext : function(keepExisting){
39572         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
39573             this.selectRow(this.last+1, keepExisting);
39574             this.grid.getView().focusRow(this.last);
39575         }
39576     },
39577
39578     /**
39579      * Selects the row that precedes the last selected row.
39580      * @param {Boolean} keepExisting (optional) True to keep existing selections
39581      */
39582     selectPrevious : function(keepExisting){
39583         if(this.last){
39584             this.selectRow(this.last-1, keepExisting);
39585             this.grid.getView().focusRow(this.last);
39586         }
39587     },
39588
39589     /**
39590      * Returns the selected records
39591      * @return {Array} Array of selected records
39592      */
39593     getSelections : function(){
39594         return [].concat(this.selections.items);
39595     },
39596
39597     /**
39598      * Returns the first selected record.
39599      * @return {Record}
39600      */
39601     getSelected : function(){
39602         return this.selections.itemAt(0);
39603     },
39604
39605
39606     /**
39607      * Clears all selections.
39608      */
39609     clearSelections : function(fast){
39610         if(this.locked) return;
39611         if(fast !== true){
39612             var ds = this.grid.dataSource;
39613             var s = this.selections;
39614             s.each(function(r){
39615                 this.deselectRow(ds.indexOfId(r.id));
39616             }, this);
39617             s.clear();
39618         }else{
39619             this.selections.clear();
39620         }
39621         this.last = false;
39622     },
39623
39624
39625     /**
39626      * Selects all rows.
39627      */
39628     selectAll : function(){
39629         if(this.locked) return;
39630         this.selections.clear();
39631         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
39632             this.selectRow(i, true);
39633         }
39634     },
39635
39636     /**
39637      * Returns True if there is a selection.
39638      * @return {Boolean}
39639      */
39640     hasSelection : function(){
39641         return this.selections.length > 0;
39642     },
39643
39644     /**
39645      * Returns True if the specified row is selected.
39646      * @param {Number/Record} record The record or index of the record to check
39647      * @return {Boolean}
39648      */
39649     isSelected : function(index){
39650         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
39651         return (r && this.selections.key(r.id) ? true : false);
39652     },
39653
39654     /**
39655      * Returns True if the specified record id is selected.
39656      * @param {String} id The id of record to check
39657      * @return {Boolean}
39658      */
39659     isIdSelected : function(id){
39660         return (this.selections.key(id) ? true : false);
39661     },
39662
39663     // private
39664     handleMouseDown : function(e, t){
39665         var view = this.grid.getView(), rowIndex;
39666         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
39667             return;
39668         };
39669         if(e.shiftKey && this.last !== false){
39670             var last = this.last;
39671             this.selectRange(last, rowIndex, e.ctrlKey);
39672             this.last = last; // reset the last
39673             view.focusRow(rowIndex);
39674         }else{
39675             var isSelected = this.isSelected(rowIndex);
39676             if(e.button !== 0 && isSelected){
39677                 view.focusRow(rowIndex);
39678             }else if(e.ctrlKey && isSelected){
39679                 this.deselectRow(rowIndex);
39680             }else if(!isSelected){
39681                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
39682                 view.focusRow(rowIndex);
39683             }
39684         }
39685         this.fireEvent("afterselectionchange", this);
39686     },
39687     // private
39688     handleDragableRowClick :  function(grid, rowIndex, e) 
39689     {
39690         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
39691             this.selectRow(rowIndex, false);
39692             grid.view.focusRow(rowIndex);
39693              this.fireEvent("afterselectionchange", this);
39694         }
39695     },
39696     
39697     /**
39698      * Selects multiple rows.
39699      * @param {Array} rows Array of the indexes of the row to select
39700      * @param {Boolean} keepExisting (optional) True to keep existing selections
39701      */
39702     selectRows : function(rows, keepExisting){
39703         if(!keepExisting){
39704             this.clearSelections();
39705         }
39706         for(var i = 0, len = rows.length; i < len; i++){
39707             this.selectRow(rows[i], true);
39708         }
39709     },
39710
39711     /**
39712      * Selects a range of rows. All rows in between startRow and endRow are also selected.
39713      * @param {Number} startRow The index of the first row in the range
39714      * @param {Number} endRow The index of the last row in the range
39715      * @param {Boolean} keepExisting (optional) True to retain existing selections
39716      */
39717     selectRange : function(startRow, endRow, keepExisting){
39718         if(this.locked) return;
39719         if(!keepExisting){
39720             this.clearSelections();
39721         }
39722         if(startRow <= endRow){
39723             for(var i = startRow; i <= endRow; i++){
39724                 this.selectRow(i, true);
39725             }
39726         }else{
39727             for(var i = startRow; i >= endRow; i--){
39728                 this.selectRow(i, true);
39729             }
39730         }
39731     },
39732
39733     /**
39734      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
39735      * @param {Number} startRow The index of the first row in the range
39736      * @param {Number} endRow The index of the last row in the range
39737      */
39738     deselectRange : function(startRow, endRow, preventViewNotify){
39739         if(this.locked) return;
39740         for(var i = startRow; i <= endRow; i++){
39741             this.deselectRow(i, preventViewNotify);
39742         }
39743     },
39744
39745     /**
39746      * Selects a row.
39747      * @param {Number} row The index of the row to select
39748      * @param {Boolean} keepExisting (optional) True to keep existing selections
39749      */
39750     selectRow : function(index, keepExisting, preventViewNotify){
39751         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
39752         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
39753             if(!keepExisting || this.singleSelect){
39754                 this.clearSelections();
39755             }
39756             var r = this.grid.dataSource.getAt(index);
39757             this.selections.add(r);
39758             this.last = this.lastActive = index;
39759             if(!preventViewNotify){
39760                 this.grid.getView().onRowSelect(index);
39761             }
39762             this.fireEvent("rowselect", this, index, r);
39763             this.fireEvent("selectionchange", this);
39764         }
39765     },
39766
39767     /**
39768      * Deselects a row.
39769      * @param {Number} row The index of the row to deselect
39770      */
39771     deselectRow : function(index, preventViewNotify){
39772         if(this.locked) return;
39773         if(this.last == index){
39774             this.last = false;
39775         }
39776         if(this.lastActive == index){
39777             this.lastActive = false;
39778         }
39779         var r = this.grid.dataSource.getAt(index);
39780         this.selections.remove(r);
39781         if(!preventViewNotify){
39782             this.grid.getView().onRowDeselect(index);
39783         }
39784         this.fireEvent("rowdeselect", this, index);
39785         this.fireEvent("selectionchange", this);
39786     },
39787
39788     // private
39789     restoreLast : function(){
39790         if(this._last){
39791             this.last = this._last;
39792         }
39793     },
39794
39795     // private
39796     acceptsNav : function(row, col, cm){
39797         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39798     },
39799
39800     // private
39801     onEditorKey : function(field, e){
39802         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
39803         if(k == e.TAB){
39804             e.stopEvent();
39805             ed.completeEdit();
39806             if(e.shiftKey){
39807                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39808             }else{
39809                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39810             }
39811         }else if(k == e.ENTER && !e.ctrlKey){
39812             e.stopEvent();
39813             ed.completeEdit();
39814             if(e.shiftKey){
39815                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
39816             }else{
39817                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
39818             }
39819         }else if(k == e.ESC){
39820             ed.cancelEdit();
39821         }
39822         if(newCell){
39823             g.startEditing(newCell[0], newCell[1]);
39824         }
39825     }
39826 });/*
39827  * Based on:
39828  * Ext JS Library 1.1.1
39829  * Copyright(c) 2006-2007, Ext JS, LLC.
39830  *
39831  * Originally Released Under LGPL - original licence link has changed is not relivant.
39832  *
39833  * Fork - LGPL
39834  * <script type="text/javascript">
39835  */
39836 /**
39837  * @class Roo.grid.CellSelectionModel
39838  * @extends Roo.grid.AbstractSelectionModel
39839  * This class provides the basic implementation for cell selection in a grid.
39840  * @constructor
39841  * @param {Object} config The object containing the configuration of this model.
39842  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
39843  */
39844 Roo.grid.CellSelectionModel = function(config){
39845     Roo.apply(this, config);
39846
39847     this.selection = null;
39848
39849     this.addEvents({
39850         /**
39851              * @event beforerowselect
39852              * Fires before a cell is selected.
39853              * @param {SelectionModel} this
39854              * @param {Number} rowIndex The selected row index
39855              * @param {Number} colIndex The selected cell index
39856              */
39857             "beforecellselect" : true,
39858         /**
39859              * @event cellselect
39860              * Fires when a cell is selected.
39861              * @param {SelectionModel} this
39862              * @param {Number} rowIndex The selected row index
39863              * @param {Number} colIndex The selected cell index
39864              */
39865             "cellselect" : true,
39866         /**
39867              * @event selectionchange
39868              * Fires when the active selection changes.
39869              * @param {SelectionModel} this
39870              * @param {Object} selection null for no selection or an object (o) with two properties
39871                 <ul>
39872                 <li>o.record: the record object for the row the selection is in</li>
39873                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
39874                 </ul>
39875              */
39876             "selectionchange" : true,
39877         /**
39878              * @event tabend
39879              * Fires when the tab (or enter) was pressed on the last editable cell
39880              * You can use this to trigger add new row.
39881              * @param {SelectionModel} this
39882              */
39883             "tabend" : true,
39884          /**
39885              * @event beforeeditnext
39886              * Fires before the next editable sell is made active
39887              * You can use this to skip to another cell or fire the tabend
39888              *    if you set cell to false
39889              * @param {Object} eventdata object : { cell : [ row, col ] } 
39890              */
39891             "beforeeditnext" : true
39892     });
39893     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
39894 };
39895
39896 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
39897     
39898     enter_is_tab: false,
39899
39900     /** @ignore */
39901     initEvents : function(){
39902         this.grid.on("mousedown", this.handleMouseDown, this);
39903         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
39904         var view = this.grid.view;
39905         view.on("refresh", this.onViewChange, this);
39906         view.on("rowupdated", this.onRowUpdated, this);
39907         view.on("beforerowremoved", this.clearSelections, this);
39908         view.on("beforerowsinserted", this.clearSelections, this);
39909         if(this.grid.isEditor){
39910             this.grid.on("beforeedit", this.beforeEdit,  this);
39911         }
39912     },
39913
39914         //private
39915     beforeEdit : function(e){
39916         this.select(e.row, e.column, false, true, e.record);
39917     },
39918
39919         //private
39920     onRowUpdated : function(v, index, r){
39921         if(this.selection && this.selection.record == r){
39922             v.onCellSelect(index, this.selection.cell[1]);
39923         }
39924     },
39925
39926         //private
39927     onViewChange : function(){
39928         this.clearSelections(true);
39929     },
39930
39931         /**
39932          * Returns the currently selected cell,.
39933          * @return {Array} The selected cell (row, column) or null if none selected.
39934          */
39935     getSelectedCell : function(){
39936         return this.selection ? this.selection.cell : null;
39937     },
39938
39939     /**
39940      * Clears all selections.
39941      * @param {Boolean} true to prevent the gridview from being notified about the change.
39942      */
39943     clearSelections : function(preventNotify){
39944         var s = this.selection;
39945         if(s){
39946             if(preventNotify !== true){
39947                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
39948             }
39949             this.selection = null;
39950             this.fireEvent("selectionchange", this, null);
39951         }
39952     },
39953
39954     /**
39955      * Returns true if there is a selection.
39956      * @return {Boolean}
39957      */
39958     hasSelection : function(){
39959         return this.selection ? true : false;
39960     },
39961
39962     /** @ignore */
39963     handleMouseDown : function(e, t){
39964         var v = this.grid.getView();
39965         if(this.isLocked()){
39966             return;
39967         };
39968         var row = v.findRowIndex(t);
39969         var cell = v.findCellIndex(t);
39970         if(row !== false && cell !== false){
39971             this.select(row, cell);
39972         }
39973     },
39974
39975     /**
39976      * Selects a cell.
39977      * @param {Number} rowIndex
39978      * @param {Number} collIndex
39979      */
39980     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
39981         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
39982             this.clearSelections();
39983             r = r || this.grid.dataSource.getAt(rowIndex);
39984             this.selection = {
39985                 record : r,
39986                 cell : [rowIndex, colIndex]
39987             };
39988             if(!preventViewNotify){
39989                 var v = this.grid.getView();
39990                 v.onCellSelect(rowIndex, colIndex);
39991                 if(preventFocus !== true){
39992                     v.focusCell(rowIndex, colIndex);
39993                 }
39994             }
39995             this.fireEvent("cellselect", this, rowIndex, colIndex);
39996             this.fireEvent("selectionchange", this, this.selection);
39997         }
39998     },
39999
40000         //private
40001     isSelectable : function(rowIndex, colIndex, cm){
40002         return !cm.isHidden(colIndex);
40003     },
40004
40005     /** @ignore */
40006     handleKeyDown : function(e){
40007         //Roo.log('Cell Sel Model handleKeyDown');
40008         if(!e.isNavKeyPress()){
40009             return;
40010         }
40011         var g = this.grid, s = this.selection;
40012         if(!s){
40013             e.stopEvent();
40014             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
40015             if(cell){
40016                 this.select(cell[0], cell[1]);
40017             }
40018             return;
40019         }
40020         var sm = this;
40021         var walk = function(row, col, step){
40022             return g.walkCells(row, col, step, sm.isSelectable,  sm);
40023         };
40024         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
40025         var newCell;
40026
40027       
40028
40029         switch(k){
40030             case e.TAB:
40031                 // handled by onEditorKey
40032                 if (g.isEditor && g.editing) {
40033                     return;
40034                 }
40035                 if(e.shiftKey) {
40036                     newCell = walk(r, c-1, -1);
40037                 } else {
40038                     newCell = walk(r, c+1, 1);
40039                 }
40040                 break;
40041             
40042             case e.DOWN:
40043                newCell = walk(r+1, c, 1);
40044                 break;
40045             
40046             case e.UP:
40047                 newCell = walk(r-1, c, -1);
40048                 break;
40049             
40050             case e.RIGHT:
40051                 newCell = walk(r, c+1, 1);
40052                 break;
40053             
40054             case e.LEFT:
40055                 newCell = walk(r, c-1, -1);
40056                 break;
40057             
40058             case e.ENTER:
40059                 
40060                 if(g.isEditor && !g.editing){
40061                    g.startEditing(r, c);
40062                    e.stopEvent();
40063                    return;
40064                 }
40065                 
40066                 
40067              break;
40068         };
40069         if(newCell){
40070             this.select(newCell[0], newCell[1]);
40071             e.stopEvent();
40072             
40073         }
40074     },
40075
40076     acceptsNav : function(row, col, cm){
40077         return !cm.isHidden(col) && cm.isCellEditable(col, row);
40078     },
40079     /**
40080      * Selects a cell.
40081      * @param {Number} field (not used) - as it's normally used as a listener
40082      * @param {Number} e - event - fake it by using
40083      *
40084      * var e = Roo.EventObjectImpl.prototype;
40085      * e.keyCode = e.TAB
40086      *
40087      * 
40088      */
40089     onEditorKey : function(field, e){
40090         
40091         var k = e.getKey(),
40092             newCell,
40093             g = this.grid,
40094             ed = g.activeEditor,
40095             forward = false;
40096         ///Roo.log('onEditorKey' + k);
40097         
40098         
40099         if (this.enter_is_tab && k == e.ENTER) {
40100             k = e.TAB;
40101         }
40102         
40103         if(k == e.TAB){
40104             if(e.shiftKey){
40105                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
40106             }else{
40107                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
40108                 forward = true;
40109             }
40110             
40111             e.stopEvent();
40112             
40113         } else if(k == e.ENTER &&  !e.ctrlKey){
40114             ed.completeEdit();
40115             e.stopEvent();
40116             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
40117         
40118                 } else if(k == e.ESC){
40119             ed.cancelEdit();
40120         }
40121                 
40122         if (newCell) {
40123             var ecall = { cell : newCell, forward : forward };
40124             this.fireEvent('beforeeditnext', ecall );
40125             newCell = ecall.cell;
40126                         forward = ecall.forward;
40127         }
40128                 
40129         if(newCell){
40130             //Roo.log('next cell after edit');
40131             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
40132         } else if (forward) {
40133             // tabbed past last
40134             this.fireEvent.defer(100, this, ['tabend',this]);
40135         }
40136     }
40137 });/*
40138  * Based on:
40139  * Ext JS Library 1.1.1
40140  * Copyright(c) 2006-2007, Ext JS, LLC.
40141  *
40142  * Originally Released Under LGPL - original licence link has changed is not relivant.
40143  *
40144  * Fork - LGPL
40145  * <script type="text/javascript">
40146  */
40147  
40148 /**
40149  * @class Roo.grid.EditorGrid
40150  * @extends Roo.grid.Grid
40151  * Class for creating and editable grid.
40152  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
40153  * The container MUST have some type of size defined for the grid to fill. The container will be 
40154  * automatically set to position relative if it isn't already.
40155  * @param {Object} dataSource The data model to bind to
40156  * @param {Object} colModel The column model with info about this grid's columns
40157  */
40158 Roo.grid.EditorGrid = function(container, config){
40159     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
40160     this.getGridEl().addClass("xedit-grid");
40161
40162     if(!this.selModel){
40163         this.selModel = new Roo.grid.CellSelectionModel();
40164     }
40165
40166     this.activeEditor = null;
40167
40168         this.addEvents({
40169             /**
40170              * @event beforeedit
40171              * Fires before cell editing is triggered. The edit event object has the following properties <br />
40172              * <ul style="padding:5px;padding-left:16px;">
40173              * <li>grid - This grid</li>
40174              * <li>record - The record being edited</li>
40175              * <li>field - The field name being edited</li>
40176              * <li>value - The value for the field being edited.</li>
40177              * <li>row - The grid row index</li>
40178              * <li>column - The grid column index</li>
40179              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
40180              * </ul>
40181              * @param {Object} e An edit event (see above for description)
40182              */
40183             "beforeedit" : true,
40184             /**
40185              * @event afteredit
40186              * Fires after a cell is edited. <br />
40187              * <ul style="padding:5px;padding-left:16px;">
40188              * <li>grid - This grid</li>
40189              * <li>record - The record being edited</li>
40190              * <li>field - The field name being edited</li>
40191              * <li>value - The value being set</li>
40192              * <li>originalValue - The original value for the field, before the edit.</li>
40193              * <li>row - The grid row index</li>
40194              * <li>column - The grid column index</li>
40195              * </ul>
40196              * @param {Object} e An edit event (see above for description)
40197              */
40198             "afteredit" : true,
40199             /**
40200              * @event validateedit
40201              * Fires after a cell is edited, but before the value is set in the record. 
40202          * You can use this to modify the value being set in the field, Return false
40203              * to cancel the change. The edit event object has the following properties <br />
40204              * <ul style="padding:5px;padding-left:16px;">
40205          * <li>editor - This editor</li>
40206              * <li>grid - This grid</li>
40207              * <li>record - The record being edited</li>
40208              * <li>field - The field name being edited</li>
40209              * <li>value - The value being set</li>
40210              * <li>originalValue - The original value for the field, before the edit.</li>
40211              * <li>row - The grid row index</li>
40212              * <li>column - The grid column index</li>
40213              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
40214              * </ul>
40215              * @param {Object} e An edit event (see above for description)
40216              */
40217             "validateedit" : true
40218         });
40219     this.on("bodyscroll", this.stopEditing,  this);
40220     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
40221 };
40222
40223 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
40224     /**
40225      * @cfg {Number} clicksToEdit
40226      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
40227      */
40228     clicksToEdit: 2,
40229
40230     // private
40231     isEditor : true,
40232     // private
40233     trackMouseOver: false, // causes very odd FF errors
40234
40235     onCellDblClick : function(g, row, col){
40236         this.startEditing(row, col);
40237     },
40238
40239     onEditComplete : function(ed, value, startValue){
40240         this.editing = false;
40241         this.activeEditor = null;
40242         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
40243         var r = ed.record;
40244         var field = this.colModel.getDataIndex(ed.col);
40245         var e = {
40246             grid: this,
40247             record: r,
40248             field: field,
40249             originalValue: startValue,
40250             value: value,
40251             row: ed.row,
40252             column: ed.col,
40253             cancel:false,
40254             editor: ed
40255         };
40256         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
40257         cell.show();
40258           
40259         if(String(value) !== String(startValue)){
40260             
40261             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
40262                 r.set(field, e.value);
40263                 // if we are dealing with a combo box..
40264                 // then we also set the 'name' colum to be the displayField
40265                 if (ed.field.displayField && ed.field.name) {
40266                     r.set(ed.field.name, ed.field.el.dom.value);
40267                 }
40268                 
40269                 delete e.cancel; //?? why!!!
40270                 this.fireEvent("afteredit", e);
40271             }
40272         } else {
40273             this.fireEvent("afteredit", e); // always fire it!
40274         }
40275         this.view.focusCell(ed.row, ed.col);
40276     },
40277
40278     /**
40279      * Starts editing the specified for the specified row/column
40280      * @param {Number} rowIndex
40281      * @param {Number} colIndex
40282      */
40283     startEditing : function(row, col){
40284         this.stopEditing();
40285         if(this.colModel.isCellEditable(col, row)){
40286             this.view.ensureVisible(row, col, true);
40287           
40288             var r = this.dataSource.getAt(row);
40289             var field = this.colModel.getDataIndex(col);
40290             var cell = Roo.get(this.view.getCell(row,col));
40291             var e = {
40292                 grid: this,
40293                 record: r,
40294                 field: field,
40295                 value: r.data[field],
40296                 row: row,
40297                 column: col,
40298                 cancel:false 
40299             };
40300             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
40301                 this.editing = true;
40302                 var ed = this.colModel.getCellEditor(col, row);
40303                 
40304                 if (!ed) {
40305                     return;
40306                 }
40307                 if(!ed.rendered){
40308                     ed.render(ed.parentEl || document.body);
40309                 }
40310                 ed.field.reset();
40311                
40312                 cell.hide();
40313                 
40314                 (function(){ // complex but required for focus issues in safari, ie and opera
40315                     ed.row = row;
40316                     ed.col = col;
40317                     ed.record = r;
40318                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
40319                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
40320                     this.activeEditor = ed;
40321                     var v = r.data[field];
40322                     ed.startEdit(this.view.getCell(row, col), v);
40323                     // combo's with 'displayField and name set
40324                     if (ed.field.displayField && ed.field.name) {
40325                         ed.field.el.dom.value = r.data[ed.field.name];
40326                     }
40327                     
40328                     
40329                 }).defer(50, this);
40330             }
40331         }
40332     },
40333         
40334     /**
40335      * Stops any active editing
40336      */
40337     stopEditing : function(){
40338         if(this.activeEditor){
40339             this.activeEditor.completeEdit();
40340         }
40341         this.activeEditor = null;
40342     },
40343         
40344          /**
40345      * Called to get grid's drag proxy text, by default returns this.ddText.
40346      * @return {String}
40347      */
40348     getDragDropText : function(){
40349         var count = this.selModel.getSelectedCell() ? 1 : 0;
40350         return String.format(this.ddText, count, count == 1 ? '' : 's');
40351     }
40352         
40353 });/*
40354  * Based on:
40355  * Ext JS Library 1.1.1
40356  * Copyright(c) 2006-2007, Ext JS, LLC.
40357  *
40358  * Originally Released Under LGPL - original licence link has changed is not relivant.
40359  *
40360  * Fork - LGPL
40361  * <script type="text/javascript">
40362  */
40363
40364 // private - not really -- you end up using it !
40365 // This is a support class used internally by the Grid components
40366
40367 /**
40368  * @class Roo.grid.GridEditor
40369  * @extends Roo.Editor
40370  * Class for creating and editable grid elements.
40371  * @param {Object} config any settings (must include field)
40372  */
40373 Roo.grid.GridEditor = function(field, config){
40374     if (!config && field.field) {
40375         config = field;
40376         field = Roo.factory(config.field, Roo.form);
40377     }
40378     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
40379     field.monitorTab = false;
40380 };
40381
40382 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
40383     
40384     /**
40385      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
40386      */
40387     
40388     alignment: "tl-tl",
40389     autoSize: "width",
40390     hideEl : false,
40391     cls: "x-small-editor x-grid-editor",
40392     shim:false,
40393     shadow:"frame"
40394 });/*
40395  * Based on:
40396  * Ext JS Library 1.1.1
40397  * Copyright(c) 2006-2007, Ext JS, LLC.
40398  *
40399  * Originally Released Under LGPL - original licence link has changed is not relivant.
40400  *
40401  * Fork - LGPL
40402  * <script type="text/javascript">
40403  */
40404   
40405
40406   
40407 Roo.grid.PropertyRecord = Roo.data.Record.create([
40408     {name:'name',type:'string'},  'value'
40409 ]);
40410
40411
40412 Roo.grid.PropertyStore = function(grid, source){
40413     this.grid = grid;
40414     this.store = new Roo.data.Store({
40415         recordType : Roo.grid.PropertyRecord
40416     });
40417     this.store.on('update', this.onUpdate,  this);
40418     if(source){
40419         this.setSource(source);
40420     }
40421     Roo.grid.PropertyStore.superclass.constructor.call(this);
40422 };
40423
40424
40425
40426 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
40427     setSource : function(o){
40428         this.source = o;
40429         this.store.removeAll();
40430         var data = [];
40431         for(var k in o){
40432             if(this.isEditableValue(o[k])){
40433                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
40434             }
40435         }
40436         this.store.loadRecords({records: data}, {}, true);
40437     },
40438
40439     onUpdate : function(ds, record, type){
40440         if(type == Roo.data.Record.EDIT){
40441             var v = record.data['value'];
40442             var oldValue = record.modified['value'];
40443             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
40444                 this.source[record.id] = v;
40445                 record.commit();
40446                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
40447             }else{
40448                 record.reject();
40449             }
40450         }
40451     },
40452
40453     getProperty : function(row){
40454        return this.store.getAt(row);
40455     },
40456
40457     isEditableValue: function(val){
40458         if(val && val instanceof Date){
40459             return true;
40460         }else if(typeof val == 'object' || typeof val == 'function'){
40461             return false;
40462         }
40463         return true;
40464     },
40465
40466     setValue : function(prop, value){
40467         this.source[prop] = value;
40468         this.store.getById(prop).set('value', value);
40469     },
40470
40471     getSource : function(){
40472         return this.source;
40473     }
40474 });
40475
40476 Roo.grid.PropertyColumnModel = function(grid, store){
40477     this.grid = grid;
40478     var g = Roo.grid;
40479     g.PropertyColumnModel.superclass.constructor.call(this, [
40480         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
40481         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
40482     ]);
40483     this.store = store;
40484     this.bselect = Roo.DomHelper.append(document.body, {
40485         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
40486             {tag: 'option', value: 'true', html: 'true'},
40487             {tag: 'option', value: 'false', html: 'false'}
40488         ]
40489     });
40490     Roo.id(this.bselect);
40491     var f = Roo.form;
40492     this.editors = {
40493         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
40494         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
40495         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
40496         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
40497         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
40498     };
40499     this.renderCellDelegate = this.renderCell.createDelegate(this);
40500     this.renderPropDelegate = this.renderProp.createDelegate(this);
40501 };
40502
40503 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
40504     
40505     
40506     nameText : 'Name',
40507     valueText : 'Value',
40508     
40509     dateFormat : 'm/j/Y',
40510     
40511     
40512     renderDate : function(dateVal){
40513         return dateVal.dateFormat(this.dateFormat);
40514     },
40515
40516     renderBool : function(bVal){
40517         return bVal ? 'true' : 'false';
40518     },
40519
40520     isCellEditable : function(colIndex, rowIndex){
40521         return colIndex == 1;
40522     },
40523
40524     getRenderer : function(col){
40525         return col == 1 ?
40526             this.renderCellDelegate : this.renderPropDelegate;
40527     },
40528
40529     renderProp : function(v){
40530         return this.getPropertyName(v);
40531     },
40532
40533     renderCell : function(val){
40534         var rv = val;
40535         if(val instanceof Date){
40536             rv = this.renderDate(val);
40537         }else if(typeof val == 'boolean'){
40538             rv = this.renderBool(val);
40539         }
40540         return Roo.util.Format.htmlEncode(rv);
40541     },
40542
40543     getPropertyName : function(name){
40544         var pn = this.grid.propertyNames;
40545         return pn && pn[name] ? pn[name] : name;
40546     },
40547
40548     getCellEditor : function(colIndex, rowIndex){
40549         var p = this.store.getProperty(rowIndex);
40550         var n = p.data['name'], val = p.data['value'];
40551         
40552         if(typeof(this.grid.customEditors[n]) == 'string'){
40553             return this.editors[this.grid.customEditors[n]];
40554         }
40555         if(typeof(this.grid.customEditors[n]) != 'undefined'){
40556             return this.grid.customEditors[n];
40557         }
40558         if(val instanceof Date){
40559             return this.editors['date'];
40560         }else if(typeof val == 'number'){
40561             return this.editors['number'];
40562         }else if(typeof val == 'boolean'){
40563             return this.editors['boolean'];
40564         }else{
40565             return this.editors['string'];
40566         }
40567     }
40568 });
40569
40570 /**
40571  * @class Roo.grid.PropertyGrid
40572  * @extends Roo.grid.EditorGrid
40573  * This class represents the  interface of a component based property grid control.
40574  * <br><br>Usage:<pre><code>
40575  var grid = new Roo.grid.PropertyGrid("my-container-id", {
40576       
40577  });
40578  // set any options
40579  grid.render();
40580  * </code></pre>
40581   
40582  * @constructor
40583  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40584  * The container MUST have some type of size defined for the grid to fill. The container will be
40585  * automatically set to position relative if it isn't already.
40586  * @param {Object} config A config object that sets properties on this grid.
40587  */
40588 Roo.grid.PropertyGrid = function(container, config){
40589     config = config || {};
40590     var store = new Roo.grid.PropertyStore(this);
40591     this.store = store;
40592     var cm = new Roo.grid.PropertyColumnModel(this, store);
40593     store.store.sort('name', 'ASC');
40594     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
40595         ds: store.store,
40596         cm: cm,
40597         enableColLock:false,
40598         enableColumnMove:false,
40599         stripeRows:false,
40600         trackMouseOver: false,
40601         clicksToEdit:1
40602     }, config));
40603     this.getGridEl().addClass('x-props-grid');
40604     this.lastEditRow = null;
40605     this.on('columnresize', this.onColumnResize, this);
40606     this.addEvents({
40607          /**
40608              * @event beforepropertychange
40609              * Fires before a property changes (return false to stop?)
40610              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40611              * @param {String} id Record Id
40612              * @param {String} newval New Value
40613          * @param {String} oldval Old Value
40614              */
40615         "beforepropertychange": true,
40616         /**
40617              * @event propertychange
40618              * Fires after a property changes
40619              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40620              * @param {String} id Record Id
40621              * @param {String} newval New Value
40622          * @param {String} oldval Old Value
40623              */
40624         "propertychange": true
40625     });
40626     this.customEditors = this.customEditors || {};
40627 };
40628 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
40629     
40630      /**
40631      * @cfg {Object} customEditors map of colnames=> custom editors.
40632      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
40633      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
40634      * false disables editing of the field.
40635          */
40636     
40637       /**
40638      * @cfg {Object} propertyNames map of property Names to their displayed value
40639          */
40640     
40641     render : function(){
40642         Roo.grid.PropertyGrid.superclass.render.call(this);
40643         this.autoSize.defer(100, this);
40644     },
40645
40646     autoSize : function(){
40647         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
40648         if(this.view){
40649             this.view.fitColumns();
40650         }
40651     },
40652
40653     onColumnResize : function(){
40654         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
40655         this.autoSize();
40656     },
40657     /**
40658      * Sets the data for the Grid
40659      * accepts a Key => Value object of all the elements avaiable.
40660      * @param {Object} data  to appear in grid.
40661      */
40662     setSource : function(source){
40663         this.store.setSource(source);
40664         //this.autoSize();
40665     },
40666     /**
40667      * Gets all the data from the grid.
40668      * @return {Object} data  data stored in grid
40669      */
40670     getSource : function(){
40671         return this.store.getSource();
40672     }
40673 });/*
40674   
40675  * Licence LGPL
40676  
40677  */
40678  
40679 /**
40680  * @class Roo.grid.Calendar
40681  * @extends Roo.util.Grid
40682  * This class extends the Grid to provide a calendar widget
40683  * <br><br>Usage:<pre><code>
40684  var grid = new Roo.grid.Calendar("my-container-id", {
40685      ds: myDataStore,
40686      cm: myColModel,
40687      selModel: mySelectionModel,
40688      autoSizeColumns: true,
40689      monitorWindowResize: false,
40690      trackMouseOver: true
40691      eventstore : real data store..
40692  });
40693  // set any options
40694  grid.render();
40695   
40696   * @constructor
40697  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40698  * The container MUST have some type of size defined for the grid to fill. The container will be
40699  * automatically set to position relative if it isn't already.
40700  * @param {Object} config A config object that sets properties on this grid.
40701  */
40702 Roo.grid.Calendar = function(container, config){
40703         // initialize the container
40704         this.container = Roo.get(container);
40705         this.container.update("");
40706         this.container.setStyle("overflow", "hidden");
40707     this.container.addClass('x-grid-container');
40708
40709     this.id = this.container.id;
40710
40711     Roo.apply(this, config);
40712     // check and correct shorthanded configs
40713     
40714     var rows = [];
40715     var d =1;
40716     for (var r = 0;r < 6;r++) {
40717         
40718         rows[r]=[];
40719         for (var c =0;c < 7;c++) {
40720             rows[r][c]= '';
40721         }
40722     }
40723     if (this.eventStore) {
40724         this.eventStore= Roo.factory(this.eventStore, Roo.data);
40725         this.eventStore.on('load',this.onLoad, this);
40726         this.eventStore.on('beforeload',this.clearEvents, this);
40727          
40728     }
40729     
40730     this.dataSource = new Roo.data.Store({
40731             proxy: new Roo.data.MemoryProxy(rows),
40732             reader: new Roo.data.ArrayReader({}, [
40733                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
40734     });
40735
40736     this.dataSource.load();
40737     this.ds = this.dataSource;
40738     this.ds.xmodule = this.xmodule || false;
40739     
40740     
40741     var cellRender = function(v,x,r)
40742     {
40743         return String.format(
40744             '<div class="fc-day  fc-widget-content"><div>' +
40745                 '<div class="fc-event-container"></div>' +
40746                 '<div class="fc-day-number">{0}</div>'+
40747                 
40748                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
40749             '</div></div>', v);
40750     
40751     }
40752     
40753     
40754     this.colModel = new Roo.grid.ColumnModel( [
40755         {
40756             xtype: 'ColumnModel',
40757             xns: Roo.grid,
40758             dataIndex : 'weekday0',
40759             header : 'Sunday',
40760             renderer : cellRender
40761         },
40762         {
40763             xtype: 'ColumnModel',
40764             xns: Roo.grid,
40765             dataIndex : 'weekday1',
40766             header : 'Monday',
40767             renderer : cellRender
40768         },
40769         {
40770             xtype: 'ColumnModel',
40771             xns: Roo.grid,
40772             dataIndex : 'weekday2',
40773             header : 'Tuesday',
40774             renderer : cellRender
40775         },
40776         {
40777             xtype: 'ColumnModel',
40778             xns: Roo.grid,
40779             dataIndex : 'weekday3',
40780             header : 'Wednesday',
40781             renderer : cellRender
40782         },
40783         {
40784             xtype: 'ColumnModel',
40785             xns: Roo.grid,
40786             dataIndex : 'weekday4',
40787             header : 'Thursday',
40788             renderer : cellRender
40789         },
40790         {
40791             xtype: 'ColumnModel',
40792             xns: Roo.grid,
40793             dataIndex : 'weekday5',
40794             header : 'Friday',
40795             renderer : cellRender
40796         },
40797         {
40798             xtype: 'ColumnModel',
40799             xns: Roo.grid,
40800             dataIndex : 'weekday6',
40801             header : 'Saturday',
40802             renderer : cellRender
40803         }
40804     ]);
40805     this.cm = this.colModel;
40806     this.cm.xmodule = this.xmodule || false;
40807  
40808         
40809           
40810     //this.selModel = new Roo.grid.CellSelectionModel();
40811     //this.sm = this.selModel;
40812     //this.selModel.init(this);
40813     
40814     
40815     if(this.width){
40816         this.container.setWidth(this.width);
40817     }
40818
40819     if(this.height){
40820         this.container.setHeight(this.height);
40821     }
40822     /** @private */
40823         this.addEvents({
40824         // raw events
40825         /**
40826          * @event click
40827          * The raw click event for the entire grid.
40828          * @param {Roo.EventObject} e
40829          */
40830         "click" : true,
40831         /**
40832          * @event dblclick
40833          * The raw dblclick event for the entire grid.
40834          * @param {Roo.EventObject} e
40835          */
40836         "dblclick" : true,
40837         /**
40838          * @event contextmenu
40839          * The raw contextmenu event for the entire grid.
40840          * @param {Roo.EventObject} e
40841          */
40842         "contextmenu" : true,
40843         /**
40844          * @event mousedown
40845          * The raw mousedown event for the entire grid.
40846          * @param {Roo.EventObject} e
40847          */
40848         "mousedown" : true,
40849         /**
40850          * @event mouseup
40851          * The raw mouseup event for the entire grid.
40852          * @param {Roo.EventObject} e
40853          */
40854         "mouseup" : true,
40855         /**
40856          * @event mouseover
40857          * The raw mouseover event for the entire grid.
40858          * @param {Roo.EventObject} e
40859          */
40860         "mouseover" : true,
40861         /**
40862          * @event mouseout
40863          * The raw mouseout event for the entire grid.
40864          * @param {Roo.EventObject} e
40865          */
40866         "mouseout" : true,
40867         /**
40868          * @event keypress
40869          * The raw keypress event for the entire grid.
40870          * @param {Roo.EventObject} e
40871          */
40872         "keypress" : true,
40873         /**
40874          * @event keydown
40875          * The raw keydown event for the entire grid.
40876          * @param {Roo.EventObject} e
40877          */
40878         "keydown" : true,
40879
40880         // custom events
40881
40882         /**
40883          * @event cellclick
40884          * Fires when a cell is clicked
40885          * @param {Grid} this
40886          * @param {Number} rowIndex
40887          * @param {Number} columnIndex
40888          * @param {Roo.EventObject} e
40889          */
40890         "cellclick" : true,
40891         /**
40892          * @event celldblclick
40893          * Fires when a cell is double clicked
40894          * @param {Grid} this
40895          * @param {Number} rowIndex
40896          * @param {Number} columnIndex
40897          * @param {Roo.EventObject} e
40898          */
40899         "celldblclick" : true,
40900         /**
40901          * @event rowclick
40902          * Fires when a row is clicked
40903          * @param {Grid} this
40904          * @param {Number} rowIndex
40905          * @param {Roo.EventObject} e
40906          */
40907         "rowclick" : true,
40908         /**
40909          * @event rowdblclick
40910          * Fires when a row is double clicked
40911          * @param {Grid} this
40912          * @param {Number} rowIndex
40913          * @param {Roo.EventObject} e
40914          */
40915         "rowdblclick" : true,
40916         /**
40917          * @event headerclick
40918          * Fires when a header is clicked
40919          * @param {Grid} this
40920          * @param {Number} columnIndex
40921          * @param {Roo.EventObject} e
40922          */
40923         "headerclick" : true,
40924         /**
40925          * @event headerdblclick
40926          * Fires when a header cell is double clicked
40927          * @param {Grid} this
40928          * @param {Number} columnIndex
40929          * @param {Roo.EventObject} e
40930          */
40931         "headerdblclick" : true,
40932         /**
40933          * @event rowcontextmenu
40934          * Fires when a row is right clicked
40935          * @param {Grid} this
40936          * @param {Number} rowIndex
40937          * @param {Roo.EventObject} e
40938          */
40939         "rowcontextmenu" : true,
40940         /**
40941          * @event cellcontextmenu
40942          * Fires when a cell is right clicked
40943          * @param {Grid} this
40944          * @param {Number} rowIndex
40945          * @param {Number} cellIndex
40946          * @param {Roo.EventObject} e
40947          */
40948          "cellcontextmenu" : true,
40949         /**
40950          * @event headercontextmenu
40951          * Fires when a header is right clicked
40952          * @param {Grid} this
40953          * @param {Number} columnIndex
40954          * @param {Roo.EventObject} e
40955          */
40956         "headercontextmenu" : true,
40957         /**
40958          * @event bodyscroll
40959          * Fires when the body element is scrolled
40960          * @param {Number} scrollLeft
40961          * @param {Number} scrollTop
40962          */
40963         "bodyscroll" : true,
40964         /**
40965          * @event columnresize
40966          * Fires when the user resizes a column
40967          * @param {Number} columnIndex
40968          * @param {Number} newSize
40969          */
40970         "columnresize" : true,
40971         /**
40972          * @event columnmove
40973          * Fires when the user moves a column
40974          * @param {Number} oldIndex
40975          * @param {Number} newIndex
40976          */
40977         "columnmove" : true,
40978         /**
40979          * @event startdrag
40980          * Fires when row(s) start being dragged
40981          * @param {Grid} this
40982          * @param {Roo.GridDD} dd The drag drop object
40983          * @param {event} e The raw browser event
40984          */
40985         "startdrag" : true,
40986         /**
40987          * @event enddrag
40988          * Fires when a drag operation is complete
40989          * @param {Grid} this
40990          * @param {Roo.GridDD} dd The drag drop object
40991          * @param {event} e The raw browser event
40992          */
40993         "enddrag" : true,
40994         /**
40995          * @event dragdrop
40996          * Fires when dragged row(s) are dropped on a valid DD target
40997          * @param {Grid} this
40998          * @param {Roo.GridDD} dd The drag drop object
40999          * @param {String} targetId The target drag drop object
41000          * @param {event} e The raw browser event
41001          */
41002         "dragdrop" : true,
41003         /**
41004          * @event dragover
41005          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
41006          * @param {Grid} this
41007          * @param {Roo.GridDD} dd The drag drop object
41008          * @param {String} targetId The target drag drop object
41009          * @param {event} e The raw browser event
41010          */
41011         "dragover" : true,
41012         /**
41013          * @event dragenter
41014          *  Fires when the dragged row(s) first cross another DD target while being dragged
41015          * @param {Grid} this
41016          * @param {Roo.GridDD} dd The drag drop object
41017          * @param {String} targetId The target drag drop object
41018          * @param {event} e The raw browser event
41019          */
41020         "dragenter" : true,
41021         /**
41022          * @event dragout
41023          * Fires when the dragged row(s) leave another DD target while being dragged
41024          * @param {Grid} this
41025          * @param {Roo.GridDD} dd The drag drop object
41026          * @param {String} targetId The target drag drop object
41027          * @param {event} e The raw browser event
41028          */
41029         "dragout" : true,
41030         /**
41031          * @event rowclass
41032          * Fires when a row is rendered, so you can change add a style to it.
41033          * @param {GridView} gridview   The grid view
41034          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
41035          */
41036         'rowclass' : true,
41037
41038         /**
41039          * @event render
41040          * Fires when the grid is rendered
41041          * @param {Grid} grid
41042          */
41043         'render' : true,
41044             /**
41045              * @event select
41046              * Fires when a date is selected
41047              * @param {DatePicker} this
41048              * @param {Date} date The selected date
41049              */
41050         'select': true,
41051         /**
41052              * @event monthchange
41053              * Fires when the displayed month changes 
41054              * @param {DatePicker} this
41055              * @param {Date} date The selected month
41056              */
41057         'monthchange': true,
41058         /**
41059              * @event evententer
41060              * Fires when mouse over an event
41061              * @param {Calendar} this
41062              * @param {event} Event
41063              */
41064         'evententer': true,
41065         /**
41066              * @event eventleave
41067              * Fires when the mouse leaves an
41068              * @param {Calendar} this
41069              * @param {event}
41070              */
41071         'eventleave': true,
41072         /**
41073              * @event eventclick
41074              * Fires when the mouse click an
41075              * @param {Calendar} this
41076              * @param {event}
41077              */
41078         'eventclick': true,
41079         /**
41080              * @event eventrender
41081              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
41082              * @param {Calendar} this
41083              * @param {data} data to be modified
41084              */
41085         'eventrender': true
41086         
41087     });
41088
41089     Roo.grid.Grid.superclass.constructor.call(this);
41090     this.on('render', function() {
41091         this.view.el.addClass('x-grid-cal'); 
41092         
41093         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
41094
41095     },this);
41096     
41097     if (!Roo.grid.Calendar.style) {
41098         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
41099             
41100             
41101             '.x-grid-cal .x-grid-col' :  {
41102                 height: 'auto !important',
41103                 'vertical-align': 'top'
41104             },
41105             '.x-grid-cal  .fc-event-hori' : {
41106                 height: '14px'
41107             }
41108              
41109             
41110         }, Roo.id());
41111     }
41112
41113     
41114     
41115 };
41116 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
41117     /**
41118      * @cfg {Store} eventStore The store that loads events.
41119      */
41120     eventStore : 25,
41121
41122      
41123     activeDate : false,
41124     startDay : 0,
41125     autoWidth : true,
41126     monitorWindowResize : false,
41127
41128     
41129     resizeColumns : function() {
41130         var col = (this.view.el.getWidth() / 7) - 3;
41131         // loop through cols, and setWidth
41132         for(var i =0 ; i < 7 ; i++){
41133             this.cm.setColumnWidth(i, col);
41134         }
41135     },
41136      setDate :function(date) {
41137         
41138         Roo.log('setDate?');
41139         
41140         this.resizeColumns();
41141         var vd = this.activeDate;
41142         this.activeDate = date;
41143 //        if(vd && this.el){
41144 //            var t = date.getTime();
41145 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
41146 //                Roo.log('using add remove');
41147 //                
41148 //                this.fireEvent('monthchange', this, date);
41149 //                
41150 //                this.cells.removeClass("fc-state-highlight");
41151 //                this.cells.each(function(c){
41152 //                   if(c.dateValue == t){
41153 //                       c.addClass("fc-state-highlight");
41154 //                       setTimeout(function(){
41155 //                            try{c.dom.firstChild.focus();}catch(e){}
41156 //                       }, 50);
41157 //                       return false;
41158 //                   }
41159 //                   return true;
41160 //                });
41161 //                return;
41162 //            }
41163 //        }
41164         
41165         var days = date.getDaysInMonth();
41166         
41167         var firstOfMonth = date.getFirstDateOfMonth();
41168         var startingPos = firstOfMonth.getDay()-this.startDay;
41169         
41170         if(startingPos < this.startDay){
41171             startingPos += 7;
41172         }
41173         
41174         var pm = date.add(Date.MONTH, -1);
41175         var prevStart = pm.getDaysInMonth()-startingPos;
41176 //        
41177         
41178         
41179         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
41180         
41181         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
41182         //this.cells.addClassOnOver('fc-state-hover');
41183         
41184         var cells = this.cells.elements;
41185         var textEls = this.textNodes;
41186         
41187         //Roo.each(cells, function(cell){
41188         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
41189         //});
41190         
41191         days += startingPos;
41192
41193         // convert everything to numbers so it's fast
41194         var day = 86400000;
41195         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
41196         //Roo.log(d);
41197         //Roo.log(pm);
41198         //Roo.log(prevStart);
41199         
41200         var today = new Date().clearTime().getTime();
41201         var sel = date.clearTime().getTime();
41202         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
41203         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
41204         var ddMatch = this.disabledDatesRE;
41205         var ddText = this.disabledDatesText;
41206         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
41207         var ddaysText = this.disabledDaysText;
41208         var format = this.format;
41209         
41210         var setCellClass = function(cal, cell){
41211             
41212             //Roo.log('set Cell Class');
41213             cell.title = "";
41214             var t = d.getTime();
41215             
41216             //Roo.log(d);
41217             
41218             
41219             cell.dateValue = t;
41220             if(t == today){
41221                 cell.className += " fc-today";
41222                 cell.className += " fc-state-highlight";
41223                 cell.title = cal.todayText;
41224             }
41225             if(t == sel){
41226                 // disable highlight in other month..
41227                 cell.className += " fc-state-highlight";
41228                 
41229             }
41230             // disabling
41231             if(t < min) {
41232                 //cell.className = " fc-state-disabled";
41233                 cell.title = cal.minText;
41234                 return;
41235             }
41236             if(t > max) {
41237                 //cell.className = " fc-state-disabled";
41238                 cell.title = cal.maxText;
41239                 return;
41240             }
41241             if(ddays){
41242                 if(ddays.indexOf(d.getDay()) != -1){
41243                     // cell.title = ddaysText;
41244                    // cell.className = " fc-state-disabled";
41245                 }
41246             }
41247             if(ddMatch && format){
41248                 var fvalue = d.dateFormat(format);
41249                 if(ddMatch.test(fvalue)){
41250                     cell.title = ddText.replace("%0", fvalue);
41251                    cell.className = " fc-state-disabled";
41252                 }
41253             }
41254             
41255             if (!cell.initialClassName) {
41256                 cell.initialClassName = cell.dom.className;
41257             }
41258             
41259             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
41260         };
41261
41262         var i = 0;
41263         
41264         for(; i < startingPos; i++) {
41265             cells[i].dayName =  (++prevStart);
41266             Roo.log(textEls[i]);
41267             d.setDate(d.getDate()+1);
41268             
41269             //cells[i].className = "fc-past fc-other-month";
41270             setCellClass(this, cells[i]);
41271         }
41272         
41273         var intDay = 0;
41274         
41275         for(; i < days; i++){
41276             intDay = i - startingPos + 1;
41277             cells[i].dayName =  (intDay);
41278             d.setDate(d.getDate()+1);
41279             
41280             cells[i].className = ''; // "x-date-active";
41281             setCellClass(this, cells[i]);
41282         }
41283         var extraDays = 0;
41284         
41285         for(; i < 42; i++) {
41286             //textEls[i].innerHTML = (++extraDays);
41287             
41288             d.setDate(d.getDate()+1);
41289             cells[i].dayName = (++extraDays);
41290             cells[i].className = "fc-future fc-other-month";
41291             setCellClass(this, cells[i]);
41292         }
41293         
41294         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
41295         
41296         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
41297         
41298         // this will cause all the cells to mis
41299         var rows= [];
41300         var i =0;
41301         for (var r = 0;r < 6;r++) {
41302             for (var c =0;c < 7;c++) {
41303                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
41304             }    
41305         }
41306         
41307         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
41308         for(i=0;i<cells.length;i++) {
41309             
41310             this.cells.elements[i].dayName = cells[i].dayName ;
41311             this.cells.elements[i].className = cells[i].className;
41312             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
41313             this.cells.elements[i].title = cells[i].title ;
41314             this.cells.elements[i].dateValue = cells[i].dateValue ;
41315         }
41316         
41317         
41318         
41319         
41320         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
41321         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
41322         
41323         ////if(totalRows != 6){
41324             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
41325            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
41326        // }
41327         
41328         this.fireEvent('monthchange', this, date);
41329         
41330         
41331     },
41332  /**
41333      * Returns the grid's SelectionModel.
41334      * @return {SelectionModel}
41335      */
41336     getSelectionModel : function(){
41337         if(!this.selModel){
41338             this.selModel = new Roo.grid.CellSelectionModel();
41339         }
41340         return this.selModel;
41341     },
41342
41343     load: function() {
41344         this.eventStore.load()
41345         
41346         
41347         
41348     },
41349     
41350     findCell : function(dt) {
41351         dt = dt.clearTime().getTime();
41352         var ret = false;
41353         this.cells.each(function(c){
41354             //Roo.log("check " +c.dateValue + '?=' + dt);
41355             if(c.dateValue == dt){
41356                 ret = c;
41357                 return false;
41358             }
41359             return true;
41360         });
41361         
41362         return ret;
41363     },
41364     
41365     findCells : function(rec) {
41366         var s = rec.data.start_dt.clone().clearTime().getTime();
41367        // Roo.log(s);
41368         var e= rec.data.end_dt.clone().clearTime().getTime();
41369        // Roo.log(e);
41370         var ret = [];
41371         this.cells.each(function(c){
41372              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
41373             
41374             if(c.dateValue > e){
41375                 return ;
41376             }
41377             if(c.dateValue < s){
41378                 return ;
41379             }
41380             ret.push(c);
41381         });
41382         
41383         return ret;    
41384     },
41385     
41386     findBestRow: function(cells)
41387     {
41388         var ret = 0;
41389         
41390         for (var i =0 ; i < cells.length;i++) {
41391             ret  = Math.max(cells[i].rows || 0,ret);
41392         }
41393         return ret;
41394         
41395     },
41396     
41397     
41398     addItem : function(rec)
41399     {
41400         // look for vertical location slot in
41401         var cells = this.findCells(rec);
41402         
41403         rec.row = this.findBestRow(cells);
41404         
41405         // work out the location.
41406         
41407         var crow = false;
41408         var rows = [];
41409         for(var i =0; i < cells.length; i++) {
41410             if (!crow) {
41411                 crow = {
41412                     start : cells[i],
41413                     end :  cells[i]
41414                 };
41415                 continue;
41416             }
41417             if (crow.start.getY() == cells[i].getY()) {
41418                 // on same row.
41419                 crow.end = cells[i];
41420                 continue;
41421             }
41422             // different row.
41423             rows.push(crow);
41424             crow = {
41425                 start: cells[i],
41426                 end : cells[i]
41427             };
41428             
41429         }
41430         
41431         rows.push(crow);
41432         rec.els = [];
41433         rec.rows = rows;
41434         rec.cells = cells;
41435         for (var i = 0; i < cells.length;i++) {
41436             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
41437             
41438         }
41439         
41440         
41441     },
41442     
41443     clearEvents: function() {
41444         
41445         if (!this.eventStore.getCount()) {
41446             return;
41447         }
41448         // reset number of rows in cells.
41449         Roo.each(this.cells.elements, function(c){
41450             c.rows = 0;
41451         });
41452         
41453         this.eventStore.each(function(e) {
41454             this.clearEvent(e);
41455         },this);
41456         
41457     },
41458     
41459     clearEvent : function(ev)
41460     {
41461         if (ev.els) {
41462             Roo.each(ev.els, function(el) {
41463                 el.un('mouseenter' ,this.onEventEnter, this);
41464                 el.un('mouseleave' ,this.onEventLeave, this);
41465                 el.remove();
41466             },this);
41467             ev.els = [];
41468         }
41469     },
41470     
41471     
41472     renderEvent : function(ev,ctr) {
41473         if (!ctr) {
41474              ctr = this.view.el.select('.fc-event-container',true).first();
41475         }
41476         
41477          
41478         this.clearEvent(ev);
41479             //code
41480        
41481         
41482         
41483         ev.els = [];
41484         var cells = ev.cells;
41485         var rows = ev.rows;
41486         this.fireEvent('eventrender', this, ev);
41487         
41488         for(var i =0; i < rows.length; i++) {
41489             
41490             cls = '';
41491             if (i == 0) {
41492                 cls += ' fc-event-start';
41493             }
41494             if ((i+1) == rows.length) {
41495                 cls += ' fc-event-end';
41496             }
41497             
41498             //Roo.log(ev.data);
41499             // how many rows should it span..
41500             var cg = this.eventTmpl.append(ctr,Roo.apply({
41501                 fccls : cls
41502                 
41503             }, ev.data) , true);
41504             
41505             
41506             cg.on('mouseenter' ,this.onEventEnter, this, ev);
41507             cg.on('mouseleave' ,this.onEventLeave, this, ev);
41508             cg.on('click', this.onEventClick, this, ev);
41509             
41510             ev.els.push(cg);
41511             
41512             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
41513             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
41514             //Roo.log(cg);
41515              
41516             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
41517             cg.setWidth(ebox.right - sbox.x -2);
41518         }
41519     },
41520     
41521     renderEvents: function()
41522     {   
41523         // first make sure there is enough space..
41524         
41525         if (!this.eventTmpl) {
41526             this.eventTmpl = new Roo.Template(
41527                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
41528                     '<div class="fc-event-inner">' +
41529                         '<span class="fc-event-time">{time}</span>' +
41530                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
41531                     '</div>' +
41532                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
41533                 '</div>'
41534             );
41535                 
41536         }
41537                
41538         
41539         
41540         this.cells.each(function(c) {
41541             //Roo.log(c.select('.fc-day-content div',true).first());
41542             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
41543         });
41544         
41545         var ctr = this.view.el.select('.fc-event-container',true).first();
41546         
41547         var cls;
41548         this.eventStore.each(function(ev){
41549             
41550             this.renderEvent(ev);
41551              
41552              
41553         }, this);
41554         this.view.layout();
41555         
41556     },
41557     
41558     onEventEnter: function (e, el,event,d) {
41559         this.fireEvent('evententer', this, el, event);
41560     },
41561     
41562     onEventLeave: function (e, el,event,d) {
41563         this.fireEvent('eventleave', this, el, event);
41564     },
41565     
41566     onEventClick: function (e, el,event,d) {
41567         this.fireEvent('eventclick', this, el, event);
41568     },
41569     
41570     onMonthChange: function () {
41571         this.store.load();
41572     },
41573     
41574     onLoad: function () {
41575         
41576         //Roo.log('calendar onload');
41577 //         
41578         if(this.eventStore.getCount() > 0){
41579             
41580            
41581             
41582             this.eventStore.each(function(d){
41583                 
41584                 
41585                 // FIXME..
41586                 var add =   d.data;
41587                 if (typeof(add.end_dt) == 'undefined')  {
41588                     Roo.log("Missing End time in calendar data: ");
41589                     Roo.log(d);
41590                     return;
41591                 }
41592                 if (typeof(add.start_dt) == 'undefined')  {
41593                     Roo.log("Missing Start time in calendar data: ");
41594                     Roo.log(d);
41595                     return;
41596                 }
41597                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
41598                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
41599                 add.id = add.id || d.id;
41600                 add.title = add.title || '??';
41601                 
41602                 this.addItem(d);
41603                 
41604              
41605             },this);
41606         }
41607         
41608         this.renderEvents();
41609     }
41610     
41611
41612 });
41613 /*
41614  grid : {
41615                 xtype: 'Grid',
41616                 xns: Roo.grid,
41617                 listeners : {
41618                     render : function ()
41619                     {
41620                         _this.grid = this;
41621                         
41622                         if (!this.view.el.hasClass('course-timesheet')) {
41623                             this.view.el.addClass('course-timesheet');
41624                         }
41625                         if (this.tsStyle) {
41626                             this.ds.load({});
41627                             return; 
41628                         }
41629                         Roo.log('width');
41630                         Roo.log(_this.grid.view.el.getWidth());
41631                         
41632                         
41633                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
41634                             '.course-timesheet .x-grid-row' : {
41635                                 height: '80px'
41636                             },
41637                             '.x-grid-row td' : {
41638                                 'vertical-align' : 0
41639                             },
41640                             '.course-edit-link' : {
41641                                 'color' : 'blue',
41642                                 'text-overflow' : 'ellipsis',
41643                                 'overflow' : 'hidden',
41644                                 'white-space' : 'nowrap',
41645                                 'cursor' : 'pointer'
41646                             },
41647                             '.sub-link' : {
41648                                 'color' : 'green'
41649                             },
41650                             '.de-act-sup-link' : {
41651                                 'color' : 'purple',
41652                                 'text-decoration' : 'line-through'
41653                             },
41654                             '.de-act-link' : {
41655                                 'color' : 'red',
41656                                 'text-decoration' : 'line-through'
41657                             },
41658                             '.course-timesheet .course-highlight' : {
41659                                 'border-top-style': 'dashed !important',
41660                                 'border-bottom-bottom': 'dashed !important'
41661                             },
41662                             '.course-timesheet .course-item' : {
41663                                 'font-family'   : 'tahoma, arial, helvetica',
41664                                 'font-size'     : '11px',
41665                                 'overflow'      : 'hidden',
41666                                 'padding-left'  : '10px',
41667                                 'padding-right' : '10px',
41668                                 'padding-top' : '10px' 
41669                             }
41670                             
41671                         }, Roo.id());
41672                                 this.ds.load({});
41673                     }
41674                 },
41675                 autoWidth : true,
41676                 monitorWindowResize : false,
41677                 cellrenderer : function(v,x,r)
41678                 {
41679                     return v;
41680                 },
41681                 sm : {
41682                     xtype: 'CellSelectionModel',
41683                     xns: Roo.grid
41684                 },
41685                 dataSource : {
41686                     xtype: 'Store',
41687                     xns: Roo.data,
41688                     listeners : {
41689                         beforeload : function (_self, options)
41690                         {
41691                             options.params = options.params || {};
41692                             options.params._month = _this.monthField.getValue();
41693                             options.params.limit = 9999;
41694                             options.params['sort'] = 'when_dt';    
41695                             options.params['dir'] = 'ASC';    
41696                             this.proxy.loadResponse = this.loadResponse;
41697                             Roo.log("load?");
41698                             //this.addColumns();
41699                         },
41700                         load : function (_self, records, options)
41701                         {
41702                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
41703                                 // if you click on the translation.. you can edit it...
41704                                 var el = Roo.get(this);
41705                                 var id = el.dom.getAttribute('data-id');
41706                                 var d = el.dom.getAttribute('data-date');
41707                                 var t = el.dom.getAttribute('data-time');
41708                                 //var id = this.child('span').dom.textContent;
41709                                 
41710                                 //Roo.log(this);
41711                                 Pman.Dialog.CourseCalendar.show({
41712                                     id : id,
41713                                     when_d : d,
41714                                     when_t : t,
41715                                     productitem_active : id ? 1 : 0
41716                                 }, function() {
41717                                     _this.grid.ds.load({});
41718                                 });
41719                            
41720                            });
41721                            
41722                            _this.panel.fireEvent('resize', [ '', '' ]);
41723                         }
41724                     },
41725                     loadResponse : function(o, success, response){
41726                             // this is overridden on before load..
41727                             
41728                             Roo.log("our code?");       
41729                             //Roo.log(success);
41730                             //Roo.log(response)
41731                             delete this.activeRequest;
41732                             if(!success){
41733                                 this.fireEvent("loadexception", this, o, response);
41734                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41735                                 return;
41736                             }
41737                             var result;
41738                             try {
41739                                 result = o.reader.read(response);
41740                             }catch(e){
41741                                 Roo.log("load exception?");
41742                                 this.fireEvent("loadexception", this, o, response, e);
41743                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41744                                 return;
41745                             }
41746                             Roo.log("ready...");        
41747                             // loop through result.records;
41748                             // and set this.tdate[date] = [] << array of records..
41749                             _this.tdata  = {};
41750                             Roo.each(result.records, function(r){
41751                                 //Roo.log(r.data);
41752                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
41753                                     _this.tdata[r.data.when_dt.format('j')] = [];
41754                                 }
41755                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
41756                             });
41757                             
41758                             //Roo.log(_this.tdata);
41759                             
41760                             result.records = [];
41761                             result.totalRecords = 6;
41762                     
41763                             // let's generate some duumy records for the rows.
41764                             //var st = _this.dateField.getValue();
41765                             
41766                             // work out monday..
41767                             //st = st.add(Date.DAY, -1 * st.format('w'));
41768                             
41769                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41770                             
41771                             var firstOfMonth = date.getFirstDayOfMonth();
41772                             var days = date.getDaysInMonth();
41773                             var d = 1;
41774                             var firstAdded = false;
41775                             for (var i = 0; i < result.totalRecords ; i++) {
41776                                 //var d= st.add(Date.DAY, i);
41777                                 var row = {};
41778                                 var added = 0;
41779                                 for(var w = 0 ; w < 7 ; w++){
41780                                     if(!firstAdded && firstOfMonth != w){
41781                                         continue;
41782                                     }
41783                                     if(d > days){
41784                                         continue;
41785                                     }
41786                                     firstAdded = true;
41787                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
41788                                     row['weekday'+w] = String.format(
41789                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
41790                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
41791                                                     d,
41792                                                     date.format('Y-m-')+dd
41793                                                 );
41794                                     added++;
41795                                     if(typeof(_this.tdata[d]) != 'undefined'){
41796                                         Roo.each(_this.tdata[d], function(r){
41797                                             var is_sub = '';
41798                                             var deactive = '';
41799                                             var id = r.id;
41800                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
41801                                             if(r.parent_id*1>0){
41802                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
41803                                                 id = r.parent_id;
41804                                             }
41805                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
41806                                                 deactive = 'de-act-link';
41807                                             }
41808                                             
41809                                             row['weekday'+w] += String.format(
41810                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
41811                                                     id, //0
41812                                                     r.product_id_name, //1
41813                                                     r.when_dt.format('h:ia'), //2
41814                                                     is_sub, //3
41815                                                     deactive, //4
41816                                                     desc // 5
41817                                             );
41818                                         });
41819                                     }
41820                                     d++;
41821                                 }
41822                                 
41823                                 // only do this if something added..
41824                                 if(added > 0){ 
41825                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
41826                                 }
41827                                 
41828                                 
41829                                 // push it twice. (second one with an hour..
41830                                 
41831                             }
41832                             //Roo.log(result);
41833                             this.fireEvent("load", this, o, o.request.arg);
41834                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
41835                         },
41836                     sortInfo : {field: 'when_dt', direction : 'ASC' },
41837                     proxy : {
41838                         xtype: 'HttpProxy',
41839                         xns: Roo.data,
41840                         method : 'GET',
41841                         url : baseURL + '/Roo/Shop_course.php'
41842                     },
41843                     reader : {
41844                         xtype: 'JsonReader',
41845                         xns: Roo.data,
41846                         id : 'id',
41847                         fields : [
41848                             {
41849                                 'name': 'id',
41850                                 'type': 'int'
41851                             },
41852                             {
41853                                 'name': 'when_dt',
41854                                 'type': 'string'
41855                             },
41856                             {
41857                                 'name': 'end_dt',
41858                                 'type': 'string'
41859                             },
41860                             {
41861                                 'name': 'parent_id',
41862                                 'type': 'int'
41863                             },
41864                             {
41865                                 'name': 'product_id',
41866                                 'type': 'int'
41867                             },
41868                             {
41869                                 'name': 'productitem_id',
41870                                 'type': 'int'
41871                             },
41872                             {
41873                                 'name': 'guid',
41874                                 'type': 'int'
41875                             }
41876                         ]
41877                     }
41878                 },
41879                 toolbar : {
41880                     xtype: 'Toolbar',
41881                     xns: Roo,
41882                     items : [
41883                         {
41884                             xtype: 'Button',
41885                             xns: Roo.Toolbar,
41886                             listeners : {
41887                                 click : function (_self, e)
41888                                 {
41889                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41890                                     sd.setMonth(sd.getMonth()-1);
41891                                     _this.monthField.setValue(sd.format('Y-m-d'));
41892                                     _this.grid.ds.load({});
41893                                 }
41894                             },
41895                             text : "Back"
41896                         },
41897                         {
41898                             xtype: 'Separator',
41899                             xns: Roo.Toolbar
41900                         },
41901                         {
41902                             xtype: 'MonthField',
41903                             xns: Roo.form,
41904                             listeners : {
41905                                 render : function (_self)
41906                                 {
41907                                     _this.monthField = _self;
41908                                    // _this.monthField.set  today
41909                                 },
41910                                 select : function (combo, date)
41911                                 {
41912                                     _this.grid.ds.load({});
41913                                 }
41914                             },
41915                             value : (function() { return new Date(); })()
41916                         },
41917                         {
41918                             xtype: 'Separator',
41919                             xns: Roo.Toolbar
41920                         },
41921                         {
41922                             xtype: 'TextItem',
41923                             xns: Roo.Toolbar,
41924                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
41925                         },
41926                         {
41927                             xtype: 'Fill',
41928                             xns: Roo.Toolbar
41929                         },
41930                         {
41931                             xtype: 'Button',
41932                             xns: Roo.Toolbar,
41933                             listeners : {
41934                                 click : function (_self, e)
41935                                 {
41936                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41937                                     sd.setMonth(sd.getMonth()+1);
41938                                     _this.monthField.setValue(sd.format('Y-m-d'));
41939                                     _this.grid.ds.load({});
41940                                 }
41941                             },
41942                             text : "Next"
41943                         }
41944                     ]
41945                 },
41946                  
41947             }
41948         };
41949         
41950         *//*
41951  * Based on:
41952  * Ext JS Library 1.1.1
41953  * Copyright(c) 2006-2007, Ext JS, LLC.
41954  *
41955  * Originally Released Under LGPL - original licence link has changed is not relivant.
41956  *
41957  * Fork - LGPL
41958  * <script type="text/javascript">
41959  */
41960  
41961 /**
41962  * @class Roo.LoadMask
41963  * A simple utility class for generically masking elements while loading data.  If the element being masked has
41964  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
41965  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
41966  * element's UpdateManager load indicator and will be destroyed after the initial load.
41967  * @constructor
41968  * Create a new LoadMask
41969  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
41970  * @param {Object} config The config object
41971  */
41972 Roo.LoadMask = function(el, config){
41973     this.el = Roo.get(el);
41974     Roo.apply(this, config);
41975     if(this.store){
41976         this.store.on('beforeload', this.onBeforeLoad, this);
41977         this.store.on('load', this.onLoad, this);
41978         this.store.on('loadexception', this.onLoadException, this);
41979         this.removeMask = false;
41980     }else{
41981         var um = this.el.getUpdateManager();
41982         um.showLoadIndicator = false; // disable the default indicator
41983         um.on('beforeupdate', this.onBeforeLoad, this);
41984         um.on('update', this.onLoad, this);
41985         um.on('failure', this.onLoad, this);
41986         this.removeMask = true;
41987     }
41988 };
41989
41990 Roo.LoadMask.prototype = {
41991     /**
41992      * @cfg {Boolean} removeMask
41993      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
41994      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
41995      */
41996     /**
41997      * @cfg {String} msg
41998      * The text to display in a centered loading message box (defaults to 'Loading...')
41999      */
42000     msg : 'Loading...',
42001     /**
42002      * @cfg {String} msgCls
42003      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
42004      */
42005     msgCls : 'x-mask-loading',
42006
42007     /**
42008      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
42009      * @type Boolean
42010      */
42011     disabled: false,
42012
42013     /**
42014      * Disables the mask to prevent it from being displayed
42015      */
42016     disable : function(){
42017        this.disabled = true;
42018     },
42019
42020     /**
42021      * Enables the mask so that it can be displayed
42022      */
42023     enable : function(){
42024         this.disabled = false;
42025     },
42026     
42027     onLoadException : function()
42028     {
42029         Roo.log(arguments);
42030         
42031         if (typeof(arguments[3]) != 'undefined') {
42032             Roo.MessageBox.alert("Error loading",arguments[3]);
42033         } 
42034         /*
42035         try {
42036             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
42037                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
42038             }   
42039         } catch(e) {
42040             
42041         }
42042         */
42043     
42044         
42045         
42046         this.el.unmask(this.removeMask);
42047     },
42048     // private
42049     onLoad : function()
42050     {
42051         this.el.unmask(this.removeMask);
42052     },
42053
42054     // private
42055     onBeforeLoad : function(){
42056         if(!this.disabled){
42057             this.el.mask(this.msg, this.msgCls);
42058         }
42059     },
42060
42061     // private
42062     destroy : function(){
42063         if(this.store){
42064             this.store.un('beforeload', this.onBeforeLoad, this);
42065             this.store.un('load', this.onLoad, this);
42066             this.store.un('loadexception', this.onLoadException, this);
42067         }else{
42068             var um = this.el.getUpdateManager();
42069             um.un('beforeupdate', this.onBeforeLoad, this);
42070             um.un('update', this.onLoad, this);
42071             um.un('failure', this.onLoad, this);
42072         }
42073     }
42074 };/*
42075  * Based on:
42076  * Ext JS Library 1.1.1
42077  * Copyright(c) 2006-2007, Ext JS, LLC.
42078  *
42079  * Originally Released Under LGPL - original licence link has changed is not relivant.
42080  *
42081  * Fork - LGPL
42082  * <script type="text/javascript">
42083  */
42084
42085
42086 /**
42087  * @class Roo.XTemplate
42088  * @extends Roo.Template
42089  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
42090 <pre><code>
42091 var t = new Roo.XTemplate(
42092         '&lt;select name="{name}"&gt;',
42093                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
42094         '&lt;/select&gt;'
42095 );
42096  
42097 // then append, applying the master template values
42098  </code></pre>
42099  *
42100  * Supported features:
42101  *
42102  *  Tags:
42103
42104 <pre><code>
42105       {a_variable} - output encoded.
42106       {a_variable.format:("Y-m-d")} - call a method on the variable
42107       {a_variable:raw} - unencoded output
42108       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
42109       {a_variable:this.method_on_template(...)} - call a method on the template object.
42110  
42111 </code></pre>
42112  *  The tpl tag:
42113 <pre><code>
42114         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
42115         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
42116         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
42117         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
42118   
42119         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
42120         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
42121 </code></pre>
42122  *      
42123  */
42124 Roo.XTemplate = function()
42125 {
42126     Roo.XTemplate.superclass.constructor.apply(this, arguments);
42127     if (this.html) {
42128         this.compile();
42129     }
42130 };
42131
42132
42133 Roo.extend(Roo.XTemplate, Roo.Template, {
42134
42135     /**
42136      * The various sub templates
42137      */
42138     tpls : false,
42139     /**
42140      *
42141      * basic tag replacing syntax
42142      * WORD:WORD()
42143      *
42144      * // you can fake an object call by doing this
42145      *  x.t:(test,tesT) 
42146      * 
42147      */
42148     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
42149
42150     /**
42151      * compile the template
42152      *
42153      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
42154      *
42155      */
42156     compile: function()
42157     {
42158         var s = this.html;
42159      
42160         s = ['<tpl>', s, '</tpl>'].join('');
42161     
42162         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
42163             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
42164             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
42165             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
42166             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
42167             m,
42168             id     = 0,
42169             tpls   = [];
42170     
42171         while(true == !!(m = s.match(re))){
42172             var forMatch   = m[0].match(nameRe),
42173                 ifMatch   = m[0].match(ifRe),
42174                 execMatch   = m[0].match(execRe),
42175                 namedMatch   = m[0].match(namedRe),
42176                 
42177                 exp  = null, 
42178                 fn   = null,
42179                 exec = null,
42180                 name = forMatch && forMatch[1] ? forMatch[1] : '';
42181                 
42182             if (ifMatch) {
42183                 // if - puts fn into test..
42184                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
42185                 if(exp){
42186                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
42187                 }
42188             }
42189             
42190             if (execMatch) {
42191                 // exec - calls a function... returns empty if true is  returned.
42192                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
42193                 if(exp){
42194                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
42195                 }
42196             }
42197             
42198             
42199             if (name) {
42200                 // for = 
42201                 switch(name){
42202                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
42203                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
42204                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
42205                 }
42206             }
42207             var uid = namedMatch ? namedMatch[1] : id;
42208             
42209             
42210             tpls.push({
42211                 id:     namedMatch ? namedMatch[1] : id,
42212                 target: name,
42213                 exec:   exec,
42214                 test:   fn,
42215                 body:   m[1] || ''
42216             });
42217             if (namedMatch) {
42218                 s = s.replace(m[0], '');
42219             } else { 
42220                 s = s.replace(m[0], '{xtpl'+ id + '}');
42221             }
42222             ++id;
42223         }
42224         this.tpls = [];
42225         for(var i = tpls.length-1; i >= 0; --i){
42226             this.compileTpl(tpls[i]);
42227             this.tpls[tpls[i].id] = tpls[i];
42228         }
42229         this.master = tpls[tpls.length-1];
42230         return this;
42231     },
42232     /**
42233      * same as applyTemplate, except it's done to one of the subTemplates
42234      * when using named templates, you can do:
42235      *
42236      * var str = pl.applySubTemplate('your-name', values);
42237      *
42238      * 
42239      * @param {Number} id of the template
42240      * @param {Object} values to apply to template
42241      * @param {Object} parent (normaly the instance of this object)
42242      */
42243     applySubTemplate : function(id, values, parent)
42244     {
42245         
42246         
42247         var t = this.tpls[id];
42248         
42249         
42250         try { 
42251             if(t.test && !t.test.call(this, values, parent)){
42252                 return '';
42253             }
42254         } catch(e) {
42255             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
42256             Roo.log(e.toString());
42257             Roo.log(t.test);
42258             return ''
42259         }
42260         try { 
42261             
42262             if(t.exec && t.exec.call(this, values, parent)){
42263                 return '';
42264             }
42265         } catch(e) {
42266             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
42267             Roo.log(e.toString());
42268             Roo.log(t.exec);
42269             return ''
42270         }
42271         try {
42272             var vs = t.target ? t.target.call(this, values, parent) : values;
42273             parent = t.target ? values : parent;
42274             if(t.target && vs instanceof Array){
42275                 var buf = [];
42276                 for(var i = 0, len = vs.length; i < len; i++){
42277                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
42278                 }
42279                 return buf.join('');
42280             }
42281             return t.compiled.call(this, vs, parent);
42282         } catch (e) {
42283             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
42284             Roo.log(e.toString());
42285             Roo.log(t.compiled);
42286             return '';
42287         }
42288     },
42289
42290     compileTpl : function(tpl)
42291     {
42292         var fm = Roo.util.Format;
42293         var useF = this.disableFormats !== true;
42294         var sep = Roo.isGecko ? "+" : ",";
42295         var undef = function(str) {
42296             Roo.log("Property not found :"  + str);
42297             return '';
42298         };
42299         
42300         var fn = function(m, name, format, args)
42301         {
42302             //Roo.log(arguments);
42303             args = args ? args.replace(/\\'/g,"'") : args;
42304             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
42305             if (typeof(format) == 'undefined') {
42306                 format= 'htmlEncode';
42307             }
42308             if (format == 'raw' ) {
42309                 format = false;
42310             }
42311             
42312             if(name.substr(0, 4) == 'xtpl'){
42313                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
42314             }
42315             
42316             // build an array of options to determine if value is undefined..
42317             
42318             // basically get 'xxxx.yyyy' then do
42319             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
42320             //    (function () { Roo.log("Property not found"); return ''; })() :
42321             //    ......
42322             
42323             var udef_ar = [];
42324             var lookfor = '';
42325             Roo.each(name.split('.'), function(st) {
42326                 lookfor += (lookfor.length ? '.': '') + st;
42327                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
42328             });
42329             
42330             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
42331             
42332             
42333             if(format && useF){
42334                 
42335                 args = args ? ',' + args : "";
42336                  
42337                 if(format.substr(0, 5) != "this."){
42338                     format = "fm." + format + '(';
42339                 }else{
42340                     format = 'this.call("'+ format.substr(5) + '", ';
42341                     args = ", values";
42342                 }
42343                 
42344                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
42345             }
42346              
42347             if (args.length) {
42348                 // called with xxyx.yuu:(test,test)
42349                 // change to ()
42350                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
42351             }
42352             // raw.. - :raw modifier..
42353             return "'"+ sep + udef_st  + name + ")"+sep+"'";
42354             
42355         };
42356         var body;
42357         // branched to use + in gecko and [].join() in others
42358         if(Roo.isGecko){
42359             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
42360                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
42361                     "';};};";
42362         }else{
42363             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
42364             body.push(tpl.body.replace(/(\r\n|\n)/g,
42365                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
42366             body.push("'].join('');};};");
42367             body = body.join('');
42368         }
42369         
42370         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
42371        
42372         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
42373         eval(body);
42374         
42375         return this;
42376     },
42377
42378     applyTemplate : function(values){
42379         return this.master.compiled.call(this, values, {});
42380         //var s = this.subs;
42381     },
42382
42383     apply : function(){
42384         return this.applyTemplate.apply(this, arguments);
42385     }
42386
42387  });
42388
42389 Roo.XTemplate.from = function(el){
42390     el = Roo.getDom(el);
42391     return new Roo.XTemplate(el.value || el.innerHTML);
42392 };