28f2d184b5ccfe905cd82c542da77158513df17a
[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         
19244         ul.on("mouseover", this.onMouseOver, this);
19245         ul.on("mouseout", this.onMouseOut, this);
19246         this.items.each(function(item){
19247             if (item.hidden) {
19248                 return;
19249             }
19250             
19251             var li = document.createElement("li");
19252             li.className = "x-menu-list-item";
19253             ul.dom.appendChild(li);
19254             item.render(li, this);
19255         }, this);
19256         this.ul = ul;
19257         this.autoWidth();
19258     },
19259
19260     // private
19261     autoWidth : function(){
19262         var el = this.el, ul = this.ul;
19263         if(!el){
19264             return;
19265         }
19266         var w = this.width;
19267         if(w){
19268             el.setWidth(w);
19269         }else if(Roo.isIE){
19270             el.setWidth(this.minWidth);
19271             var t = el.dom.offsetWidth; // force recalc
19272             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19273         }
19274     },
19275
19276     // private
19277     delayAutoWidth : function(){
19278         if(this.rendered){
19279             if(!this.awTask){
19280                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19281             }
19282             this.awTask.delay(20);
19283         }
19284     },
19285
19286     // private
19287     findTargetItem : function(e){
19288         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19289         if(t && t.menuItemId){
19290             return this.items.get(t.menuItemId);
19291         }
19292     },
19293
19294     // private
19295     onClick : function(e){
19296         Roo.log("menu.onClick");
19297         var t = this.findTargetItem(e);
19298         if(!t){
19299             return;
19300         }
19301         Roo.log(e);
19302         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
19303             if(t == this.activeItem && t.shouldDeactivate(e)){
19304                 this.activeItem.deactivate();
19305                 delete this.activeItem;
19306                 return;
19307             }
19308             if(t.canActivate){
19309                 this.setActiveItem(t, true);
19310             }
19311             return;
19312             
19313             
19314         }
19315         
19316         t.onClick(e);
19317         this.fireEvent("click", this, t, e);
19318     },
19319
19320     // private
19321     setActiveItem : function(item, autoExpand){
19322         if(item != this.activeItem){
19323             if(this.activeItem){
19324                 this.activeItem.deactivate();
19325             }
19326             this.activeItem = item;
19327             item.activate(autoExpand);
19328         }else if(autoExpand){
19329             item.expandMenu();
19330         }
19331     },
19332
19333     // private
19334     tryActivate : function(start, step){
19335         var items = this.items;
19336         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19337             var item = items.get(i);
19338             if(!item.disabled && item.canActivate){
19339                 this.setActiveItem(item, false);
19340                 return item;
19341             }
19342         }
19343         return false;
19344     },
19345
19346     // private
19347     onMouseOver : function(e){
19348         var t;
19349         if(t = this.findTargetItem(e)){
19350             if(t.canActivate && !t.disabled){
19351                 this.setActiveItem(t, true);
19352             }
19353         }
19354         this.fireEvent("mouseover", this, e, t);
19355     },
19356
19357     // private
19358     onMouseOut : function(e){
19359         var t;
19360         if(t = this.findTargetItem(e)){
19361             if(t == this.activeItem && t.shouldDeactivate(e)){
19362                 this.activeItem.deactivate();
19363                 delete this.activeItem;
19364             }
19365         }
19366         this.fireEvent("mouseout", this, e, t);
19367     },
19368
19369     /**
19370      * Read-only.  Returns true if the menu is currently displayed, else false.
19371      * @type Boolean
19372      */
19373     isVisible : function(){
19374         return this.el && !this.hidden;
19375     },
19376
19377     /**
19378      * Displays this menu relative to another element
19379      * @param {String/HTMLElement/Roo.Element} element The element to align to
19380      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19381      * the element (defaults to this.defaultAlign)
19382      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19383      */
19384     show : function(el, pos, parentMenu){
19385         this.parentMenu = parentMenu;
19386         if(!this.el){
19387             this.render();
19388         }
19389         this.fireEvent("beforeshow", this);
19390         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19391     },
19392
19393     /**
19394      * Displays this menu at a specific xy position
19395      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19396      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19397      */
19398     showAt : function(xy, parentMenu, /* private: */_e){
19399         this.parentMenu = parentMenu;
19400         if(!this.el){
19401             this.render();
19402         }
19403         if(_e !== false){
19404             this.fireEvent("beforeshow", this);
19405             xy = this.el.adjustForConstraints(xy);
19406         }
19407         this.el.setXY(xy);
19408         this.el.show();
19409         this.hidden = false;
19410         this.focus();
19411         this.fireEvent("show", this);
19412     },
19413
19414     focus : function(){
19415         if(!this.hidden){
19416             this.doFocus.defer(50, this);
19417         }
19418     },
19419
19420     doFocus : function(){
19421         if(!this.hidden){
19422             this.focusEl.focus();
19423         }
19424     },
19425
19426     /**
19427      * Hides this menu and optionally all parent menus
19428      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19429      */
19430     hide : function(deep){
19431         if(this.el && this.isVisible()){
19432             this.fireEvent("beforehide", this);
19433             if(this.activeItem){
19434                 this.activeItem.deactivate();
19435                 this.activeItem = null;
19436             }
19437             this.el.hide();
19438             this.hidden = true;
19439             this.fireEvent("hide", this);
19440         }
19441         if(deep === true && this.parentMenu){
19442             this.parentMenu.hide(true);
19443         }
19444     },
19445
19446     /**
19447      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19448      * Any of the following are valid:
19449      * <ul>
19450      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19451      * <li>An HTMLElement object which will be converted to a menu item</li>
19452      * <li>A menu item config object that will be created as a new menu item</li>
19453      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19454      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19455      * </ul>
19456      * Usage:
19457      * <pre><code>
19458 // Create the menu
19459 var menu = new Roo.menu.Menu();
19460
19461 // Create a menu item to add by reference
19462 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19463
19464 // Add a bunch of items at once using different methods.
19465 // Only the last item added will be returned.
19466 var item = menu.add(
19467     menuItem,                // add existing item by ref
19468     'Dynamic Item',          // new TextItem
19469     '-',                     // new separator
19470     { text: 'Config Item' }  // new item by config
19471 );
19472 </code></pre>
19473      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19474      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19475      */
19476     add : function(){
19477         var a = arguments, l = a.length, item;
19478         for(var i = 0; i < l; i++){
19479             var el = a[i];
19480             if ((typeof(el) == "object") && el.xtype && el.xns) {
19481                 el = Roo.factory(el, Roo.menu);
19482             }
19483             
19484             if(el.render){ // some kind of Item
19485                 item = this.addItem(el);
19486             }else if(typeof el == "string"){ // string
19487                 if(el == "separator" || el == "-"){
19488                     item = this.addSeparator();
19489                 }else{
19490                     item = this.addText(el);
19491                 }
19492             }else if(el.tagName || el.el){ // element
19493                 item = this.addElement(el);
19494             }else if(typeof el == "object"){ // must be menu item config?
19495                 item = this.addMenuItem(el);
19496             }
19497         }
19498         return item;
19499     },
19500
19501     /**
19502      * Returns this menu's underlying {@link Roo.Element} object
19503      * @return {Roo.Element} The element
19504      */
19505     getEl : function(){
19506         if(!this.el){
19507             this.render();
19508         }
19509         return this.el;
19510     },
19511
19512     /**
19513      * Adds a separator bar to the menu
19514      * @return {Roo.menu.Item} The menu item that was added
19515      */
19516     addSeparator : function(){
19517         return this.addItem(new Roo.menu.Separator());
19518     },
19519
19520     /**
19521      * Adds an {@link Roo.Element} object to the menu
19522      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19523      * @return {Roo.menu.Item} The menu item that was added
19524      */
19525     addElement : function(el){
19526         return this.addItem(new Roo.menu.BaseItem(el));
19527     },
19528
19529     /**
19530      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19531      * @param {Roo.menu.Item} item The menu item to add
19532      * @return {Roo.menu.Item} The menu item that was added
19533      */
19534     addItem : function(item){
19535         this.items.add(item);
19536         if(this.ul){
19537             var li = document.createElement("li");
19538             li.className = "x-menu-list-item";
19539             this.ul.dom.appendChild(li);
19540             item.render(li, this);
19541             this.delayAutoWidth();
19542         }
19543         return item;
19544     },
19545
19546     /**
19547      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19548      * @param {Object} config A MenuItem config object
19549      * @return {Roo.menu.Item} The menu item that was added
19550      */
19551     addMenuItem : function(config){
19552         if(!(config instanceof Roo.menu.Item)){
19553             if(typeof config.checked == "boolean"){ // must be check menu item config?
19554                 config = new Roo.menu.CheckItem(config);
19555             }else{
19556                 config = new Roo.menu.Item(config);
19557             }
19558         }
19559         return this.addItem(config);
19560     },
19561
19562     /**
19563      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19564      * @param {String} text The text to display in the menu item
19565      * @return {Roo.menu.Item} The menu item that was added
19566      */
19567     addText : function(text){
19568         return this.addItem(new Roo.menu.TextItem({ text : text }));
19569     },
19570
19571     /**
19572      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19573      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19574      * @param {Roo.menu.Item} item The menu item to add
19575      * @return {Roo.menu.Item} The menu item that was added
19576      */
19577     insert : function(index, item){
19578         this.items.insert(index, item);
19579         if(this.ul){
19580             var li = document.createElement("li");
19581             li.className = "x-menu-list-item";
19582             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19583             item.render(li, this);
19584             this.delayAutoWidth();
19585         }
19586         return item;
19587     },
19588
19589     /**
19590      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19591      * @param {Roo.menu.Item} item The menu item to remove
19592      */
19593     remove : function(item){
19594         this.items.removeKey(item.id);
19595         item.destroy();
19596     },
19597
19598     /**
19599      * Removes and destroys all items in the menu
19600      */
19601     removeAll : function(){
19602         var f;
19603         while(f = this.items.first()){
19604             this.remove(f);
19605         }
19606     }
19607 });
19608
19609 // MenuNav is a private utility class used internally by the Menu
19610 Roo.menu.MenuNav = function(menu){
19611     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19612     this.scope = this.menu = menu;
19613 };
19614
19615 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19616     doRelay : function(e, h){
19617         var k = e.getKey();
19618         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19619             this.menu.tryActivate(0, 1);
19620             return false;
19621         }
19622         return h.call(this.scope || this, e, this.menu);
19623     },
19624
19625     up : function(e, m){
19626         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19627             m.tryActivate(m.items.length-1, -1);
19628         }
19629     },
19630
19631     down : function(e, m){
19632         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19633             m.tryActivate(0, 1);
19634         }
19635     },
19636
19637     right : function(e, m){
19638         if(m.activeItem){
19639             m.activeItem.expandMenu(true);
19640         }
19641     },
19642
19643     left : function(e, m){
19644         m.hide();
19645         if(m.parentMenu && m.parentMenu.activeItem){
19646             m.parentMenu.activeItem.activate();
19647         }
19648     },
19649
19650     enter : function(e, m){
19651         if(m.activeItem){
19652             e.stopPropagation();
19653             m.activeItem.onClick(e);
19654             m.fireEvent("click", this, m.activeItem);
19655             return true;
19656         }
19657     }
19658 });/*
19659  * Based on:
19660  * Ext JS Library 1.1.1
19661  * Copyright(c) 2006-2007, Ext JS, LLC.
19662  *
19663  * Originally Released Under LGPL - original licence link has changed is not relivant.
19664  *
19665  * Fork - LGPL
19666  * <script type="text/javascript">
19667  */
19668  
19669 /**
19670  * @class Roo.menu.MenuMgr
19671  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19672  * @singleton
19673  */
19674 Roo.menu.MenuMgr = function(){
19675    var menus, active, groups = {}, attached = false, lastShow = new Date();
19676
19677    // private - called when first menu is created
19678    function init(){
19679        menus = {};
19680        active = new Roo.util.MixedCollection();
19681        Roo.get(document).addKeyListener(27, function(){
19682            if(active.length > 0){
19683                hideAll();
19684            }
19685        });
19686    }
19687
19688    // private
19689    function hideAll(){
19690        if(active && active.length > 0){
19691            var c = active.clone();
19692            c.each(function(m){
19693                m.hide();
19694            });
19695        }
19696    }
19697
19698    // private
19699    function onHide(m){
19700        active.remove(m);
19701        if(active.length < 1){
19702            Roo.get(document).un("mousedown", onMouseDown);
19703            attached = false;
19704        }
19705    }
19706
19707    // private
19708    function onShow(m){
19709        var last = active.last();
19710        lastShow = new Date();
19711        active.add(m);
19712        if(!attached){
19713            Roo.get(document).on("mousedown", onMouseDown);
19714            attached = true;
19715        }
19716        if(m.parentMenu){
19717           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19718           m.parentMenu.activeChild = m;
19719        }else if(last && last.isVisible()){
19720           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19721        }
19722    }
19723
19724    // private
19725    function onBeforeHide(m){
19726        if(m.activeChild){
19727            m.activeChild.hide();
19728        }
19729        if(m.autoHideTimer){
19730            clearTimeout(m.autoHideTimer);
19731            delete m.autoHideTimer;
19732        }
19733    }
19734
19735    // private
19736    function onBeforeShow(m){
19737        var pm = m.parentMenu;
19738        if(!pm && !m.allowOtherMenus){
19739            hideAll();
19740        }else if(pm && pm.activeChild && active != m){
19741            pm.activeChild.hide();
19742        }
19743    }
19744
19745    // private
19746    function onMouseDown(e){
19747        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
19748            hideAll();
19749        }
19750    }
19751
19752    // private
19753    function onBeforeCheck(mi, state){
19754        if(state){
19755            var g = groups[mi.group];
19756            for(var i = 0, l = g.length; i < l; i++){
19757                if(g[i] != mi){
19758                    g[i].setChecked(false);
19759                }
19760            }
19761        }
19762    }
19763
19764    return {
19765
19766        /**
19767         * Hides all menus that are currently visible
19768         */
19769        hideAll : function(){
19770             hideAll();  
19771        },
19772
19773        // private
19774        register : function(menu){
19775            if(!menus){
19776                init();
19777            }
19778            menus[menu.id] = menu;
19779            menu.on("beforehide", onBeforeHide);
19780            menu.on("hide", onHide);
19781            menu.on("beforeshow", onBeforeShow);
19782            menu.on("show", onShow);
19783            var g = menu.group;
19784            if(g && menu.events["checkchange"]){
19785                if(!groups[g]){
19786                    groups[g] = [];
19787                }
19788                groups[g].push(menu);
19789                menu.on("checkchange", onCheck);
19790            }
19791        },
19792
19793         /**
19794          * Returns a {@link Roo.menu.Menu} object
19795          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
19796          * be used to generate and return a new Menu instance.
19797          */
19798        get : function(menu){
19799            if(typeof menu == "string"){ // menu id
19800                return menus[menu];
19801            }else if(menu.events){  // menu instance
19802                return menu;
19803            }else if(typeof menu.length == 'number'){ // array of menu items?
19804                return new Roo.menu.Menu({items:menu});
19805            }else{ // otherwise, must be a config
19806                return new Roo.menu.Menu(menu);
19807            }
19808        },
19809
19810        // private
19811        unregister : function(menu){
19812            delete menus[menu.id];
19813            menu.un("beforehide", onBeforeHide);
19814            menu.un("hide", onHide);
19815            menu.un("beforeshow", onBeforeShow);
19816            menu.un("show", onShow);
19817            var g = menu.group;
19818            if(g && menu.events["checkchange"]){
19819                groups[g].remove(menu);
19820                menu.un("checkchange", onCheck);
19821            }
19822        },
19823
19824        // private
19825        registerCheckable : function(menuItem){
19826            var g = menuItem.group;
19827            if(g){
19828                if(!groups[g]){
19829                    groups[g] = [];
19830                }
19831                groups[g].push(menuItem);
19832                menuItem.on("beforecheckchange", onBeforeCheck);
19833            }
19834        },
19835
19836        // private
19837        unregisterCheckable : function(menuItem){
19838            var g = menuItem.group;
19839            if(g){
19840                groups[g].remove(menuItem);
19841                menuItem.un("beforecheckchange", onBeforeCheck);
19842            }
19843        }
19844    };
19845 }();/*
19846  * Based on:
19847  * Ext JS Library 1.1.1
19848  * Copyright(c) 2006-2007, Ext JS, LLC.
19849  *
19850  * Originally Released Under LGPL - original licence link has changed is not relivant.
19851  *
19852  * Fork - LGPL
19853  * <script type="text/javascript">
19854  */
19855  
19856
19857 /**
19858  * @class Roo.menu.BaseItem
19859  * @extends Roo.Component
19860  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
19861  * management and base configuration options shared by all menu components.
19862  * @constructor
19863  * Creates a new BaseItem
19864  * @param {Object} config Configuration options
19865  */
19866 Roo.menu.BaseItem = function(config){
19867     Roo.menu.BaseItem.superclass.constructor.call(this, config);
19868
19869     this.addEvents({
19870         /**
19871          * @event click
19872          * Fires when this item is clicked
19873          * @param {Roo.menu.BaseItem} this
19874          * @param {Roo.EventObject} e
19875          */
19876         click: true,
19877         /**
19878          * @event activate
19879          * Fires when this item is activated
19880          * @param {Roo.menu.BaseItem} this
19881          */
19882         activate : true,
19883         /**
19884          * @event deactivate
19885          * Fires when this item is deactivated
19886          * @param {Roo.menu.BaseItem} this
19887          */
19888         deactivate : true
19889     });
19890
19891     if(this.handler){
19892         this.on("click", this.handler, this.scope, true);
19893     }
19894 };
19895
19896 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
19897     /**
19898      * @cfg {Function} handler
19899      * A function that will handle the click event of this menu item (defaults to undefined)
19900      */
19901     /**
19902      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
19903      */
19904     canActivate : false,
19905     
19906      /**
19907      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
19908      */
19909     hidden: false,
19910     
19911     /**
19912      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
19913      */
19914     activeClass : "x-menu-item-active",
19915     /**
19916      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
19917      */
19918     hideOnClick : true,
19919     /**
19920      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
19921      */
19922     hideDelay : 100,
19923
19924     // private
19925     ctype: "Roo.menu.BaseItem",
19926
19927     // private
19928     actionMode : "container",
19929
19930     // private
19931     render : function(container, parentMenu){
19932         this.parentMenu = parentMenu;
19933         Roo.menu.BaseItem.superclass.render.call(this, container);
19934         this.container.menuItemId = this.id;
19935     },
19936
19937     // private
19938     onRender : function(container, position){
19939         this.el = Roo.get(this.el);
19940         container.dom.appendChild(this.el.dom);
19941     },
19942
19943     // private
19944     onClick : function(e){
19945         if(!this.disabled && this.fireEvent("click", this, e) !== false
19946                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
19947             this.handleClick(e);
19948         }else{
19949             e.stopEvent();
19950         }
19951     },
19952
19953     // private
19954     activate : function(){
19955         if(this.disabled){
19956             return false;
19957         }
19958         var li = this.container;
19959         li.addClass(this.activeClass);
19960         this.region = li.getRegion().adjust(2, 2, -2, -2);
19961         this.fireEvent("activate", this);
19962         return true;
19963     },
19964
19965     // private
19966     deactivate : function(){
19967         this.container.removeClass(this.activeClass);
19968         this.fireEvent("deactivate", this);
19969     },
19970
19971     // private
19972     shouldDeactivate : function(e){
19973         return !this.region || !this.region.contains(e.getPoint());
19974     },
19975
19976     // private
19977     handleClick : function(e){
19978         if(this.hideOnClick){
19979             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
19980         }
19981     },
19982
19983     // private
19984     expandMenu : function(autoActivate){
19985         // do nothing
19986     },
19987
19988     // private
19989     hideMenu : function(){
19990         // do nothing
19991     }
19992 });/*
19993  * Based on:
19994  * Ext JS Library 1.1.1
19995  * Copyright(c) 2006-2007, Ext JS, LLC.
19996  *
19997  * Originally Released Under LGPL - original licence link has changed is not relivant.
19998  *
19999  * Fork - LGPL
20000  * <script type="text/javascript">
20001  */
20002  
20003 /**
20004  * @class Roo.menu.Adapter
20005  * @extends Roo.menu.BaseItem
20006  * 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.
20007  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20008  * @constructor
20009  * Creates a new Adapter
20010  * @param {Object} config Configuration options
20011  */
20012 Roo.menu.Adapter = function(component, config){
20013     Roo.menu.Adapter.superclass.constructor.call(this, config);
20014     this.component = component;
20015 };
20016 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20017     // private
20018     canActivate : true,
20019
20020     // private
20021     onRender : function(container, position){
20022         this.component.render(container);
20023         this.el = this.component.getEl();
20024     },
20025
20026     // private
20027     activate : function(){
20028         if(this.disabled){
20029             return false;
20030         }
20031         this.component.focus();
20032         this.fireEvent("activate", this);
20033         return true;
20034     },
20035
20036     // private
20037     deactivate : function(){
20038         this.fireEvent("deactivate", this);
20039     },
20040
20041     // private
20042     disable : function(){
20043         this.component.disable();
20044         Roo.menu.Adapter.superclass.disable.call(this);
20045     },
20046
20047     // private
20048     enable : function(){
20049         this.component.enable();
20050         Roo.menu.Adapter.superclass.enable.call(this);
20051     }
20052 });/*
20053  * Based on:
20054  * Ext JS Library 1.1.1
20055  * Copyright(c) 2006-2007, Ext JS, LLC.
20056  *
20057  * Originally Released Under LGPL - original licence link has changed is not relivant.
20058  *
20059  * Fork - LGPL
20060  * <script type="text/javascript">
20061  */
20062
20063 /**
20064  * @class Roo.menu.TextItem
20065  * @extends Roo.menu.BaseItem
20066  * Adds a static text string to a menu, usually used as either a heading or group separator.
20067  * Note: old style constructor with text is still supported.
20068  * 
20069  * @constructor
20070  * Creates a new TextItem
20071  * @param {Object} cfg Configuration
20072  */
20073 Roo.menu.TextItem = function(cfg){
20074     if (typeof(cfg) == 'string') {
20075         this.text = cfg;
20076     } else {
20077         Roo.apply(this,cfg);
20078     }
20079     
20080     Roo.menu.TextItem.superclass.constructor.call(this);
20081 };
20082
20083 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20084     /**
20085      * @cfg {Boolean} text Text to show on item.
20086      */
20087     text : '',
20088     
20089     /**
20090      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20091      */
20092     hideOnClick : false,
20093     /**
20094      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20095      */
20096     itemCls : "x-menu-text",
20097
20098     // private
20099     onRender : function(){
20100         var s = document.createElement("span");
20101         s.className = this.itemCls;
20102         s.innerHTML = this.text;
20103         this.el = s;
20104         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20105     }
20106 });/*
20107  * Based on:
20108  * Ext JS Library 1.1.1
20109  * Copyright(c) 2006-2007, Ext JS, LLC.
20110  *
20111  * Originally Released Under LGPL - original licence link has changed is not relivant.
20112  *
20113  * Fork - LGPL
20114  * <script type="text/javascript">
20115  */
20116
20117 /**
20118  * @class Roo.menu.Separator
20119  * @extends Roo.menu.BaseItem
20120  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20121  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20122  * @constructor
20123  * @param {Object} config Configuration options
20124  */
20125 Roo.menu.Separator = function(config){
20126     Roo.menu.Separator.superclass.constructor.call(this, config);
20127 };
20128
20129 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20130     /**
20131      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20132      */
20133     itemCls : "x-menu-sep",
20134     /**
20135      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20136      */
20137     hideOnClick : false,
20138
20139     // private
20140     onRender : function(li){
20141         var s = document.createElement("span");
20142         s.className = this.itemCls;
20143         s.innerHTML = "&#160;";
20144         this.el = s;
20145         li.addClass("x-menu-sep-li");
20146         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20147     }
20148 });/*
20149  * Based on:
20150  * Ext JS Library 1.1.1
20151  * Copyright(c) 2006-2007, Ext JS, LLC.
20152  *
20153  * Originally Released Under LGPL - original licence link has changed is not relivant.
20154  *
20155  * Fork - LGPL
20156  * <script type="text/javascript">
20157  */
20158 /**
20159  * @class Roo.menu.Item
20160  * @extends Roo.menu.BaseItem
20161  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20162  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20163  * activation and click handling.
20164  * @constructor
20165  * Creates a new Item
20166  * @param {Object} config Configuration options
20167  */
20168 Roo.menu.Item = function(config){
20169     Roo.menu.Item.superclass.constructor.call(this, config);
20170     if(this.menu){
20171         this.menu = Roo.menu.MenuMgr.get(this.menu);
20172     }
20173 };
20174 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20175     
20176     /**
20177      * @cfg {String} text
20178      * The text to show on the menu item.
20179      */
20180     text: '',
20181      /**
20182      * @cfg {String} HTML to render in menu
20183      * The text to show on the menu item (HTML version).
20184      */
20185     html: '',
20186     /**
20187      * @cfg {String} icon
20188      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20189      */
20190     icon: undefined,
20191     /**
20192      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20193      */
20194     itemCls : "x-menu-item",
20195     /**
20196      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20197      */
20198     canActivate : true,
20199     /**
20200      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20201      */
20202     showDelay: 200,
20203     // doc'd in BaseItem
20204     hideDelay: 200,
20205
20206     // private
20207     ctype: "Roo.menu.Item",
20208     
20209     // private
20210     onRender : function(container, position){
20211         var el = document.createElement("a");
20212         el.hideFocus = true;
20213         el.unselectable = "on";
20214         el.href = this.href || "#";
20215         if(this.hrefTarget){
20216             el.target = this.hrefTarget;
20217         }
20218         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20219         
20220         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20221         
20222         el.innerHTML = String.format(
20223                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20224                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20225         this.el = el;
20226         Roo.menu.Item.superclass.onRender.call(this, container, position);
20227     },
20228
20229     /**
20230      * Sets the text to display in this menu item
20231      * @param {String} text The text to display
20232      * @param {Boolean} isHTML true to indicate text is pure html.
20233      */
20234     setText : function(text, isHTML){
20235         if (isHTML) {
20236             this.html = text;
20237         } else {
20238             this.text = text;
20239             this.html = '';
20240         }
20241         if(this.rendered){
20242             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20243      
20244             this.el.update(String.format(
20245                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20246                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20247             this.parentMenu.autoWidth();
20248         }
20249     },
20250
20251     // private
20252     handleClick : function(e){
20253         if(!this.href){ // if no link defined, stop the event automatically
20254             e.stopEvent();
20255         }
20256         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20257     },
20258
20259     // private
20260     activate : function(autoExpand){
20261         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20262             this.focus();
20263             if(autoExpand){
20264                 this.expandMenu();
20265             }
20266         }
20267         return true;
20268     },
20269
20270     // private
20271     shouldDeactivate : function(e){
20272         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20273             if(this.menu && this.menu.isVisible()){
20274                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20275             }
20276             return true;
20277         }
20278         return false;
20279     },
20280
20281     // private
20282     deactivate : function(){
20283         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20284         this.hideMenu();
20285     },
20286
20287     // private
20288     expandMenu : function(autoActivate){
20289         if(!this.disabled && this.menu){
20290             clearTimeout(this.hideTimer);
20291             delete this.hideTimer;
20292             if(!this.menu.isVisible() && !this.showTimer){
20293                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20294             }else if (this.menu.isVisible() && autoActivate){
20295                 this.menu.tryActivate(0, 1);
20296             }
20297         }
20298     },
20299
20300     // private
20301     deferExpand : function(autoActivate){
20302         delete this.showTimer;
20303         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20304         if(autoActivate){
20305             this.menu.tryActivate(0, 1);
20306         }
20307     },
20308
20309     // private
20310     hideMenu : function(){
20311         clearTimeout(this.showTimer);
20312         delete this.showTimer;
20313         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20314             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20315         }
20316     },
20317
20318     // private
20319     deferHide : function(){
20320         delete this.hideTimer;
20321         this.menu.hide();
20322     }
20323 });/*
20324  * Based on:
20325  * Ext JS Library 1.1.1
20326  * Copyright(c) 2006-2007, Ext JS, LLC.
20327  *
20328  * Originally Released Under LGPL - original licence link has changed is not relivant.
20329  *
20330  * Fork - LGPL
20331  * <script type="text/javascript">
20332  */
20333  
20334 /**
20335  * @class Roo.menu.CheckItem
20336  * @extends Roo.menu.Item
20337  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20338  * @constructor
20339  * Creates a new CheckItem
20340  * @param {Object} config Configuration options
20341  */
20342 Roo.menu.CheckItem = function(config){
20343     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20344     this.addEvents({
20345         /**
20346          * @event beforecheckchange
20347          * Fires before the checked value is set, providing an opportunity to cancel if needed
20348          * @param {Roo.menu.CheckItem} this
20349          * @param {Boolean} checked The new checked value that will be set
20350          */
20351         "beforecheckchange" : true,
20352         /**
20353          * @event checkchange
20354          * Fires after the checked value has been set
20355          * @param {Roo.menu.CheckItem} this
20356          * @param {Boolean} checked The checked value that was set
20357          */
20358         "checkchange" : true
20359     });
20360     if(this.checkHandler){
20361         this.on('checkchange', this.checkHandler, this.scope);
20362     }
20363 };
20364 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20365     /**
20366      * @cfg {String} group
20367      * All check items with the same group name will automatically be grouped into a single-select
20368      * radio button group (defaults to '')
20369      */
20370     /**
20371      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20372      */
20373     itemCls : "x-menu-item x-menu-check-item",
20374     /**
20375      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20376      */
20377     groupClass : "x-menu-group-item",
20378
20379     /**
20380      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20381      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20382      * initialized with checked = true will be rendered as checked.
20383      */
20384     checked: false,
20385
20386     // private
20387     ctype: "Roo.menu.CheckItem",
20388
20389     // private
20390     onRender : function(c){
20391         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20392         if(this.group){
20393             this.el.addClass(this.groupClass);
20394         }
20395         Roo.menu.MenuMgr.registerCheckable(this);
20396         if(this.checked){
20397             this.checked = false;
20398             this.setChecked(true, true);
20399         }
20400     },
20401
20402     // private
20403     destroy : function(){
20404         if(this.rendered){
20405             Roo.menu.MenuMgr.unregisterCheckable(this);
20406         }
20407         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20408     },
20409
20410     /**
20411      * Set the checked state of this item
20412      * @param {Boolean} checked The new checked value
20413      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20414      */
20415     setChecked : function(state, suppressEvent){
20416         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20417             if(this.container){
20418                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20419             }
20420             this.checked = state;
20421             if(suppressEvent !== true){
20422                 this.fireEvent("checkchange", this, state);
20423             }
20424         }
20425     },
20426
20427     // private
20428     handleClick : function(e){
20429        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20430            this.setChecked(!this.checked);
20431        }
20432        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20433     }
20434 });/*
20435  * Based on:
20436  * Ext JS Library 1.1.1
20437  * Copyright(c) 2006-2007, Ext JS, LLC.
20438  *
20439  * Originally Released Under LGPL - original licence link has changed is not relivant.
20440  *
20441  * Fork - LGPL
20442  * <script type="text/javascript">
20443  */
20444  
20445 /**
20446  * @class Roo.menu.DateItem
20447  * @extends Roo.menu.Adapter
20448  * A menu item that wraps the {@link Roo.DatPicker} component.
20449  * @constructor
20450  * Creates a new DateItem
20451  * @param {Object} config Configuration options
20452  */
20453 Roo.menu.DateItem = function(config){
20454     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20455     /** The Roo.DatePicker object @type Roo.DatePicker */
20456     this.picker = this.component;
20457     this.addEvents({select: true});
20458     
20459     this.picker.on("render", function(picker){
20460         picker.getEl().swallowEvent("click");
20461         picker.container.addClass("x-menu-date-item");
20462     });
20463
20464     this.picker.on("select", this.onSelect, this);
20465 };
20466
20467 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20468     // private
20469     onSelect : function(picker, date){
20470         this.fireEvent("select", this, date, picker);
20471         Roo.menu.DateItem.superclass.handleClick.call(this);
20472     }
20473 });/*
20474  * Based on:
20475  * Ext JS Library 1.1.1
20476  * Copyright(c) 2006-2007, Ext JS, LLC.
20477  *
20478  * Originally Released Under LGPL - original licence link has changed is not relivant.
20479  *
20480  * Fork - LGPL
20481  * <script type="text/javascript">
20482  */
20483  
20484 /**
20485  * @class Roo.menu.ColorItem
20486  * @extends Roo.menu.Adapter
20487  * A menu item that wraps the {@link Roo.ColorPalette} component.
20488  * @constructor
20489  * Creates a new ColorItem
20490  * @param {Object} config Configuration options
20491  */
20492 Roo.menu.ColorItem = function(config){
20493     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20494     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20495     this.palette = this.component;
20496     this.relayEvents(this.palette, ["select"]);
20497     if(this.selectHandler){
20498         this.on('select', this.selectHandler, this.scope);
20499     }
20500 };
20501 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20502  * Based on:
20503  * Ext JS Library 1.1.1
20504  * Copyright(c) 2006-2007, Ext JS, LLC.
20505  *
20506  * Originally Released Under LGPL - original licence link has changed is not relivant.
20507  *
20508  * Fork - LGPL
20509  * <script type="text/javascript">
20510  */
20511  
20512
20513 /**
20514  * @class Roo.menu.DateMenu
20515  * @extends Roo.menu.Menu
20516  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20517  * @constructor
20518  * Creates a new DateMenu
20519  * @param {Object} config Configuration options
20520  */
20521 Roo.menu.DateMenu = function(config){
20522     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20523     this.plain = true;
20524     var di = new Roo.menu.DateItem(config);
20525     this.add(di);
20526     /**
20527      * The {@link Roo.DatePicker} instance for this DateMenu
20528      * @type DatePicker
20529      */
20530     this.picker = di.picker;
20531     /**
20532      * @event select
20533      * @param {DatePicker} picker
20534      * @param {Date} date
20535      */
20536     this.relayEvents(di, ["select"]);
20537     this.on('beforeshow', function(){
20538         if(this.picker){
20539             this.picker.hideMonthPicker(false);
20540         }
20541     }, this);
20542 };
20543 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20544     cls:'x-date-menu'
20545 });/*
20546  * Based on:
20547  * Ext JS Library 1.1.1
20548  * Copyright(c) 2006-2007, Ext JS, LLC.
20549  *
20550  * Originally Released Under LGPL - original licence link has changed is not relivant.
20551  *
20552  * Fork - LGPL
20553  * <script type="text/javascript">
20554  */
20555  
20556
20557 /**
20558  * @class Roo.menu.ColorMenu
20559  * @extends Roo.menu.Menu
20560  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20561  * @constructor
20562  * Creates a new ColorMenu
20563  * @param {Object} config Configuration options
20564  */
20565 Roo.menu.ColorMenu = function(config){
20566     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20567     this.plain = true;
20568     var ci = new Roo.menu.ColorItem(config);
20569     this.add(ci);
20570     /**
20571      * The {@link Roo.ColorPalette} instance for this ColorMenu
20572      * @type ColorPalette
20573      */
20574     this.palette = ci.palette;
20575     /**
20576      * @event select
20577      * @param {ColorPalette} palette
20578      * @param {String} color
20579      */
20580     this.relayEvents(ci, ["select"]);
20581 };
20582 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20583  * Based on:
20584  * Ext JS Library 1.1.1
20585  * Copyright(c) 2006-2007, Ext JS, LLC.
20586  *
20587  * Originally Released Under LGPL - original licence link has changed is not relivant.
20588  *
20589  * Fork - LGPL
20590  * <script type="text/javascript">
20591  */
20592  
20593 /**
20594  * @class Roo.form.Field
20595  * @extends Roo.BoxComponent
20596  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20597  * @constructor
20598  * Creates a new Field
20599  * @param {Object} config Configuration options
20600  */
20601 Roo.form.Field = function(config){
20602     Roo.form.Field.superclass.constructor.call(this, config);
20603 };
20604
20605 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20606     /**
20607      * @cfg {String} fieldLabel Label to use when rendering a form.
20608      */
20609        /**
20610      * @cfg {String} qtip Mouse over tip
20611      */
20612      
20613     /**
20614      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20615      */
20616     invalidClass : "x-form-invalid",
20617     /**
20618      * @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")
20619      */
20620     invalidText : "The value in this field is invalid",
20621     /**
20622      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20623      */
20624     focusClass : "x-form-focus",
20625     /**
20626      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20627       automatic validation (defaults to "keyup").
20628      */
20629     validationEvent : "keyup",
20630     /**
20631      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20632      */
20633     validateOnBlur : true,
20634     /**
20635      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20636      */
20637     validationDelay : 250,
20638     /**
20639      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20640      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20641      */
20642     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
20643     /**
20644      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20645      */
20646     fieldClass : "x-form-field",
20647     /**
20648      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20649      *<pre>
20650 Value         Description
20651 -----------   ----------------------------------------------------------------------
20652 qtip          Display a quick tip when the user hovers over the field
20653 title         Display a default browser title attribute popup
20654 under         Add a block div beneath the field containing the error text
20655 side          Add an error icon to the right of the field with a popup on hover
20656 [element id]  Add the error text directly to the innerHTML of the specified element
20657 </pre>
20658      */
20659     msgTarget : 'qtip',
20660     /**
20661      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20662      */
20663     msgFx : 'normal',
20664
20665     /**
20666      * @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.
20667      */
20668     readOnly : false,
20669
20670     /**
20671      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20672      */
20673     disabled : false,
20674
20675     /**
20676      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20677      */
20678     inputType : undefined,
20679     
20680     /**
20681      * @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).
20682          */
20683         tabIndex : undefined,
20684         
20685     // private
20686     isFormField : true,
20687
20688     // private
20689     hasFocus : false,
20690     /**
20691      * @property {Roo.Element} fieldEl
20692      * Element Containing the rendered Field (with label etc.)
20693      */
20694     /**
20695      * @cfg {Mixed} value A value to initialize this field with.
20696      */
20697     value : undefined,
20698
20699     /**
20700      * @cfg {String} name The field's HTML name attribute.
20701      */
20702     /**
20703      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20704      */
20705
20706         // private ??
20707         initComponent : function(){
20708         Roo.form.Field.superclass.initComponent.call(this);
20709         this.addEvents({
20710             /**
20711              * @event focus
20712              * Fires when this field receives input focus.
20713              * @param {Roo.form.Field} this
20714              */
20715             focus : true,
20716             /**
20717              * @event blur
20718              * Fires when this field loses input focus.
20719              * @param {Roo.form.Field} this
20720              */
20721             blur : true,
20722             /**
20723              * @event specialkey
20724              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20725              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20726              * @param {Roo.form.Field} this
20727              * @param {Roo.EventObject} e The event object
20728              */
20729             specialkey : true,
20730             /**
20731              * @event change
20732              * Fires just before the field blurs if the field value has changed.
20733              * @param {Roo.form.Field} this
20734              * @param {Mixed} newValue The new value
20735              * @param {Mixed} oldValue The original value
20736              */
20737             change : true,
20738             /**
20739              * @event invalid
20740              * Fires after the field has been marked as invalid.
20741              * @param {Roo.form.Field} this
20742              * @param {String} msg The validation message
20743              */
20744             invalid : true,
20745             /**
20746              * @event valid
20747              * Fires after the field has been validated with no errors.
20748              * @param {Roo.form.Field} this
20749              */
20750             valid : true,
20751              /**
20752              * @event keyup
20753              * Fires after the key up
20754              * @param {Roo.form.Field} this
20755              * @param {Roo.EventObject}  e The event Object
20756              */
20757             keyup : true
20758         });
20759     },
20760
20761     /**
20762      * Returns the name attribute of the field if available
20763      * @return {String} name The field name
20764      */
20765     getName: function(){
20766          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
20767     },
20768
20769     // private
20770     onRender : function(ct, position){
20771         Roo.form.Field.superclass.onRender.call(this, ct, position);
20772         if(!this.el){
20773             var cfg = this.getAutoCreate();
20774             if(!cfg.name){
20775                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
20776             }
20777             if (!cfg.name.length) {
20778                 delete cfg.name;
20779             }
20780             if(this.inputType){
20781                 cfg.type = this.inputType;
20782             }
20783             this.el = ct.createChild(cfg, position);
20784         }
20785         var type = this.el.dom.type;
20786         if(type){
20787             if(type == 'password'){
20788                 type = 'text';
20789             }
20790             this.el.addClass('x-form-'+type);
20791         }
20792         if(this.readOnly){
20793             this.el.dom.readOnly = true;
20794         }
20795         if(this.tabIndex !== undefined){
20796             this.el.dom.setAttribute('tabIndex', this.tabIndex);
20797         }
20798
20799         this.el.addClass([this.fieldClass, this.cls]);
20800         this.initValue();
20801     },
20802
20803     /**
20804      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
20805      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
20806      * @return {Roo.form.Field} this
20807      */
20808     applyTo : function(target){
20809         this.allowDomMove = false;
20810         this.el = Roo.get(target);
20811         this.render(this.el.dom.parentNode);
20812         return this;
20813     },
20814
20815     // private
20816     initValue : function(){
20817         if(this.value !== undefined){
20818             this.setValue(this.value);
20819         }else if(this.el.dom.value.length > 0){
20820             this.setValue(this.el.dom.value);
20821         }
20822     },
20823
20824     /**
20825      * Returns true if this field has been changed since it was originally loaded and is not disabled.
20826      */
20827     isDirty : function() {
20828         if(this.disabled) {
20829             return false;
20830         }
20831         return String(this.getValue()) !== String(this.originalValue);
20832     },
20833
20834     // private
20835     afterRender : function(){
20836         Roo.form.Field.superclass.afterRender.call(this);
20837         this.initEvents();
20838     },
20839
20840     // private
20841     fireKey : function(e){
20842         //Roo.log('field ' + e.getKey());
20843         if(e.isNavKeyPress()){
20844             this.fireEvent("specialkey", this, e);
20845         }
20846     },
20847
20848     /**
20849      * Resets the current field value to the originally loaded value and clears any validation messages
20850      */
20851     reset : function(){
20852         this.setValue(this.resetValue);
20853         this.clearInvalid();
20854     },
20855
20856     // private
20857     initEvents : function(){
20858         // safari killled keypress - so keydown is now used..
20859         this.el.on("keydown" , this.fireKey,  this);
20860         this.el.on("focus", this.onFocus,  this);
20861         this.el.on("blur", this.onBlur,  this);
20862         this.el.relayEvent('keyup', this);
20863
20864         // reference to original value for reset
20865         this.originalValue = this.getValue();
20866         this.resetValue =  this.getValue();
20867     },
20868
20869     // private
20870     onFocus : function(){
20871         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20872             this.el.addClass(this.focusClass);
20873         }
20874         if(!this.hasFocus){
20875             this.hasFocus = true;
20876             this.startValue = this.getValue();
20877             this.fireEvent("focus", this);
20878         }
20879     },
20880
20881     beforeBlur : Roo.emptyFn,
20882
20883     // private
20884     onBlur : function(){
20885         this.beforeBlur();
20886         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20887             this.el.removeClass(this.focusClass);
20888         }
20889         this.hasFocus = false;
20890         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
20891             this.validate();
20892         }
20893         var v = this.getValue();
20894         if(String(v) !== String(this.startValue)){
20895             this.fireEvent('change', this, v, this.startValue);
20896         }
20897         this.fireEvent("blur", this);
20898     },
20899
20900     /**
20901      * Returns whether or not the field value is currently valid
20902      * @param {Boolean} preventMark True to disable marking the field invalid
20903      * @return {Boolean} True if the value is valid, else false
20904      */
20905     isValid : function(preventMark){
20906         if(this.disabled){
20907             return true;
20908         }
20909         var restore = this.preventMark;
20910         this.preventMark = preventMark === true;
20911         var v = this.validateValue(this.processValue(this.getRawValue()));
20912         this.preventMark = restore;
20913         return v;
20914     },
20915
20916     /**
20917      * Validates the field value
20918      * @return {Boolean} True if the value is valid, else false
20919      */
20920     validate : function(){
20921         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
20922             this.clearInvalid();
20923             return true;
20924         }
20925         return false;
20926     },
20927
20928     processValue : function(value){
20929         return value;
20930     },
20931
20932     // private
20933     // Subclasses should provide the validation implementation by overriding this
20934     validateValue : function(value){
20935         return true;
20936     },
20937
20938     /**
20939      * Mark this field as invalid
20940      * @param {String} msg The validation message
20941      */
20942     markInvalid : function(msg){
20943         if(!this.rendered || this.preventMark){ // not rendered
20944             return;
20945         }
20946         
20947         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
20948         
20949         obj.el.addClass(this.invalidClass);
20950         msg = msg || this.invalidText;
20951         switch(this.msgTarget){
20952             case 'qtip':
20953                 obj.el.dom.qtip = msg;
20954                 obj.el.dom.qclass = 'x-form-invalid-tip';
20955                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
20956                     Roo.QuickTips.enable();
20957                 }
20958                 break;
20959             case 'title':
20960                 this.el.dom.title = msg;
20961                 break;
20962             case 'under':
20963                 if(!this.errorEl){
20964                     var elp = this.el.findParent('.x-form-element', 5, true);
20965                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
20966                     this.errorEl.setWidth(elp.getWidth(true)-20);
20967                 }
20968                 this.errorEl.update(msg);
20969                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
20970                 break;
20971             case 'side':
20972                 if(!this.errorIcon){
20973                     var elp = this.el.findParent('.x-form-element', 5, true);
20974                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
20975                 }
20976                 this.alignErrorIcon();
20977                 this.errorIcon.dom.qtip = msg;
20978                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
20979                 this.errorIcon.show();
20980                 this.on('resize', this.alignErrorIcon, this);
20981                 break;
20982             default:
20983                 var t = Roo.getDom(this.msgTarget);
20984                 t.innerHTML = msg;
20985                 t.style.display = this.msgDisplay;
20986                 break;
20987         }
20988         this.fireEvent('invalid', this, msg);
20989     },
20990
20991     // private
20992     alignErrorIcon : function(){
20993         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
20994     },
20995
20996     /**
20997      * Clear any invalid styles/messages for this field
20998      */
20999     clearInvalid : function(){
21000         if(!this.rendered || this.preventMark){ // not rendered
21001             return;
21002         }
21003         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
21004         
21005         obj.el.removeClass(this.invalidClass);
21006         switch(this.msgTarget){
21007             case 'qtip':
21008                 obj.el.dom.qtip = '';
21009                 break;
21010             case 'title':
21011                 this.el.dom.title = '';
21012                 break;
21013             case 'under':
21014                 if(this.errorEl){
21015                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21016                 }
21017                 break;
21018             case 'side':
21019                 if(this.errorIcon){
21020                     this.errorIcon.dom.qtip = '';
21021                     this.errorIcon.hide();
21022                     this.un('resize', this.alignErrorIcon, this);
21023                 }
21024                 break;
21025             default:
21026                 var t = Roo.getDom(this.msgTarget);
21027                 t.innerHTML = '';
21028                 t.style.display = 'none';
21029                 break;
21030         }
21031         this.fireEvent('valid', this);
21032     },
21033
21034     /**
21035      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21036      * @return {Mixed} value The field value
21037      */
21038     getRawValue : function(){
21039         var v = this.el.getValue();
21040         
21041         return v;
21042     },
21043
21044     /**
21045      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21046      * @return {Mixed} value The field value
21047      */
21048     getValue : function(){
21049         var v = this.el.getValue();
21050          
21051         return v;
21052     },
21053
21054     /**
21055      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21056      * @param {Mixed} value The value to set
21057      */
21058     setRawValue : function(v){
21059         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21060     },
21061
21062     /**
21063      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21064      * @param {Mixed} value The value to set
21065      */
21066     setValue : function(v){
21067         this.value = v;
21068         if(this.rendered){
21069             this.el.dom.value = (v === null || v === undefined ? '' : v);
21070              this.validate();
21071         }
21072     },
21073
21074     adjustSize : function(w, h){
21075         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21076         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21077         return s;
21078     },
21079
21080     adjustWidth : function(tag, w){
21081         tag = tag.toLowerCase();
21082         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21083             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21084                 if(tag == 'input'){
21085                     return w + 2;
21086                 }
21087                 if(tag == 'textarea'){
21088                     return w-2;
21089                 }
21090             }else if(Roo.isOpera){
21091                 if(tag == 'input'){
21092                     return w + 2;
21093                 }
21094                 if(tag == 'textarea'){
21095                     return w-2;
21096                 }
21097             }
21098         }
21099         return w;
21100     }
21101 });
21102
21103
21104 // anything other than normal should be considered experimental
21105 Roo.form.Field.msgFx = {
21106     normal : {
21107         show: function(msgEl, f){
21108             msgEl.setDisplayed('block');
21109         },
21110
21111         hide : function(msgEl, f){
21112             msgEl.setDisplayed(false).update('');
21113         }
21114     },
21115
21116     slide : {
21117         show: function(msgEl, f){
21118             msgEl.slideIn('t', {stopFx:true});
21119         },
21120
21121         hide : function(msgEl, f){
21122             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21123         }
21124     },
21125
21126     slideRight : {
21127         show: function(msgEl, f){
21128             msgEl.fixDisplay();
21129             msgEl.alignTo(f.el, 'tl-tr');
21130             msgEl.slideIn('l', {stopFx:true});
21131         },
21132
21133         hide : function(msgEl, f){
21134             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21135         }
21136     }
21137 };/*
21138  * Based on:
21139  * Ext JS Library 1.1.1
21140  * Copyright(c) 2006-2007, Ext JS, LLC.
21141  *
21142  * Originally Released Under LGPL - original licence link has changed is not relivant.
21143  *
21144  * Fork - LGPL
21145  * <script type="text/javascript">
21146  */
21147  
21148
21149 /**
21150  * @class Roo.form.TextField
21151  * @extends Roo.form.Field
21152  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21153  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21154  * @constructor
21155  * Creates a new TextField
21156  * @param {Object} config Configuration options
21157  */
21158 Roo.form.TextField = function(config){
21159     Roo.form.TextField.superclass.constructor.call(this, config);
21160     this.addEvents({
21161         /**
21162          * @event autosize
21163          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21164          * according to the default logic, but this event provides a hook for the developer to apply additional
21165          * logic at runtime to resize the field if needed.
21166              * @param {Roo.form.Field} this This text field
21167              * @param {Number} width The new field width
21168              */
21169         autosize : true
21170     });
21171 };
21172
21173 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21174     /**
21175      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21176      */
21177     grow : false,
21178     /**
21179      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21180      */
21181     growMin : 30,
21182     /**
21183      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21184      */
21185     growMax : 800,
21186     /**
21187      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21188      */
21189     vtype : null,
21190     /**
21191      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21192      */
21193     maskRe : null,
21194     /**
21195      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21196      */
21197     disableKeyFilter : false,
21198     /**
21199      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21200      */
21201     allowBlank : true,
21202     /**
21203      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21204      */
21205     minLength : 0,
21206     /**
21207      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21208      */
21209     maxLength : Number.MAX_VALUE,
21210     /**
21211      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21212      */
21213     minLengthText : "The minimum length for this field is {0}",
21214     /**
21215      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21216      */
21217     maxLengthText : "The maximum length for this field is {0}",
21218     /**
21219      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21220      */
21221     selectOnFocus : false,
21222     /**
21223      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21224      */
21225     blankText : "This field is required",
21226     /**
21227      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21228      * If available, this function will be called only after the basic validators all return true, and will be passed the
21229      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21230      */
21231     validator : null,
21232     /**
21233      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21234      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21235      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21236      */
21237     regex : null,
21238     /**
21239      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21240      */
21241     regexText : "",
21242     /**
21243      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
21244      */
21245     emptyText : null,
21246    
21247
21248     // private
21249     initEvents : function()
21250     {
21251         if (this.emptyText) {
21252             this.el.attr('placeholder', this.emptyText);
21253         }
21254         
21255         Roo.form.TextField.superclass.initEvents.call(this);
21256         if(this.validationEvent == 'keyup'){
21257             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21258             this.el.on('keyup', this.filterValidation, this);
21259         }
21260         else if(this.validationEvent !== false){
21261             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21262         }
21263         
21264         if(this.selectOnFocus){
21265             this.on("focus", this.preFocus, this);
21266             
21267         }
21268         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21269             this.el.on("keypress", this.filterKeys, this);
21270         }
21271         if(this.grow){
21272             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21273             this.el.on("click", this.autoSize,  this);
21274         }
21275         if(this.el.is('input[type=password]') && Roo.isSafari){
21276             this.el.on('keydown', this.SafariOnKeyDown, this);
21277         }
21278     },
21279
21280     processValue : function(value){
21281         if(this.stripCharsRe){
21282             var newValue = value.replace(this.stripCharsRe, '');
21283             if(newValue !== value){
21284                 this.setRawValue(newValue);
21285                 return newValue;
21286             }
21287         }
21288         return value;
21289     },
21290
21291     filterValidation : function(e){
21292         if(!e.isNavKeyPress()){
21293             this.validationTask.delay(this.validationDelay);
21294         }
21295     },
21296
21297     // private
21298     onKeyUp : function(e){
21299         if(!e.isNavKeyPress()){
21300             this.autoSize();
21301         }
21302     },
21303
21304     /**
21305      * Resets the current field value to the originally-loaded value and clears any validation messages.
21306      *  
21307      */
21308     reset : function(){
21309         Roo.form.TextField.superclass.reset.call(this);
21310        
21311     },
21312
21313     
21314     // private
21315     preFocus : function(){
21316         
21317         if(this.selectOnFocus){
21318             this.el.dom.select();
21319         }
21320     },
21321
21322     
21323     // private
21324     filterKeys : function(e){
21325         var k = e.getKey();
21326         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21327             return;
21328         }
21329         var c = e.getCharCode(), cc = String.fromCharCode(c);
21330         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21331             return;
21332         }
21333         if(!this.maskRe.test(cc)){
21334             e.stopEvent();
21335         }
21336     },
21337
21338     setValue : function(v){
21339         
21340         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21341         
21342         this.autoSize();
21343     },
21344
21345     /**
21346      * Validates a value according to the field's validation rules and marks the field as invalid
21347      * if the validation fails
21348      * @param {Mixed} value The value to validate
21349      * @return {Boolean} True if the value is valid, else false
21350      */
21351     validateValue : function(value){
21352         if(value.length < 1)  { // if it's blank
21353              if(this.allowBlank){
21354                 this.clearInvalid();
21355                 return true;
21356              }else{
21357                 this.markInvalid(this.blankText);
21358                 return false;
21359              }
21360         }
21361         if(value.length < this.minLength){
21362             this.markInvalid(String.format(this.minLengthText, this.minLength));
21363             return false;
21364         }
21365         if(value.length > this.maxLength){
21366             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21367             return false;
21368         }
21369         if(this.vtype){
21370             var vt = Roo.form.VTypes;
21371             if(!vt[this.vtype](value, this)){
21372                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21373                 return false;
21374             }
21375         }
21376         if(typeof this.validator == "function"){
21377             var msg = this.validator(value);
21378             if(msg !== true){
21379                 this.markInvalid(msg);
21380                 return false;
21381             }
21382         }
21383         if(this.regex && !this.regex.test(value)){
21384             this.markInvalid(this.regexText);
21385             return false;
21386         }
21387         return true;
21388     },
21389
21390     /**
21391      * Selects text in this field
21392      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21393      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21394      */
21395     selectText : function(start, end){
21396         var v = this.getRawValue();
21397         if(v.length > 0){
21398             start = start === undefined ? 0 : start;
21399             end = end === undefined ? v.length : end;
21400             var d = this.el.dom;
21401             if(d.setSelectionRange){
21402                 d.setSelectionRange(start, end);
21403             }else if(d.createTextRange){
21404                 var range = d.createTextRange();
21405                 range.moveStart("character", start);
21406                 range.moveEnd("character", v.length-end);
21407                 range.select();
21408             }
21409         }
21410     },
21411
21412     /**
21413      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21414      * This only takes effect if grow = true, and fires the autosize event.
21415      */
21416     autoSize : function(){
21417         if(!this.grow || !this.rendered){
21418             return;
21419         }
21420         if(!this.metrics){
21421             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21422         }
21423         var el = this.el;
21424         var v = el.dom.value;
21425         var d = document.createElement('div');
21426         d.appendChild(document.createTextNode(v));
21427         v = d.innerHTML;
21428         d = null;
21429         v += "&#160;";
21430         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21431         this.el.setWidth(w);
21432         this.fireEvent("autosize", this, w);
21433     },
21434     
21435     // private
21436     SafariOnKeyDown : function(event)
21437     {
21438         // this is a workaround for a password hang bug on chrome/ webkit.
21439         
21440         var isSelectAll = false;
21441         
21442         if(this.el.dom.selectionEnd > 0){
21443             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
21444         }
21445         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
21446             event.preventDefault();
21447             this.setValue('');
21448             return;
21449         }
21450         
21451         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
21452             
21453             event.preventDefault();
21454             // this is very hacky as keydown always get's upper case.
21455             
21456             var cc = String.fromCharCode(event.getCharCode());
21457             
21458             
21459             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
21460             
21461         }
21462         
21463         
21464     }
21465 });/*
21466  * Based on:
21467  * Ext JS Library 1.1.1
21468  * Copyright(c) 2006-2007, Ext JS, LLC.
21469  *
21470  * Originally Released Under LGPL - original licence link has changed is not relivant.
21471  *
21472  * Fork - LGPL
21473  * <script type="text/javascript">
21474  */
21475  
21476 /**
21477  * @class Roo.form.Hidden
21478  * @extends Roo.form.TextField
21479  * Simple Hidden element used on forms 
21480  * 
21481  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21482  * 
21483  * @constructor
21484  * Creates a new Hidden form element.
21485  * @param {Object} config Configuration options
21486  */
21487
21488
21489
21490 // easy hidden field...
21491 Roo.form.Hidden = function(config){
21492     Roo.form.Hidden.superclass.constructor.call(this, config);
21493 };
21494   
21495 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21496     fieldLabel:      '',
21497     inputType:      'hidden',
21498     width:          50,
21499     allowBlank:     true,
21500     labelSeparator: '',
21501     hidden:         true,
21502     itemCls :       'x-form-item-display-none'
21503
21504
21505 });
21506
21507
21508 /*
21509  * Based on:
21510  * Ext JS Library 1.1.1
21511  * Copyright(c) 2006-2007, Ext JS, LLC.
21512  *
21513  * Originally Released Under LGPL - original licence link has changed is not relivant.
21514  *
21515  * Fork - LGPL
21516  * <script type="text/javascript">
21517  */
21518  
21519 /**
21520  * @class Roo.form.TriggerField
21521  * @extends Roo.form.TextField
21522  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21523  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21524  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21525  * for which you can provide a custom implementation.  For example:
21526  * <pre><code>
21527 var trigger = new Roo.form.TriggerField();
21528 trigger.onTriggerClick = myTriggerFn;
21529 trigger.applyTo('my-field');
21530 </code></pre>
21531  *
21532  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21533  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21534  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21535  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21536  * @constructor
21537  * Create a new TriggerField.
21538  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21539  * to the base TextField)
21540  */
21541 Roo.form.TriggerField = function(config){
21542     this.mimicing = false;
21543     Roo.form.TriggerField.superclass.constructor.call(this, config);
21544 };
21545
21546 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21547     /**
21548      * @cfg {String} triggerClass A CSS class to apply to the trigger
21549      */
21550     /**
21551      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21552      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21553      */
21554     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
21555     /**
21556      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21557      */
21558     hideTrigger:false,
21559
21560     /** @cfg {Boolean} grow @hide */
21561     /** @cfg {Number} growMin @hide */
21562     /** @cfg {Number} growMax @hide */
21563
21564     /**
21565      * @hide 
21566      * @method
21567      */
21568     autoSize: Roo.emptyFn,
21569     // private
21570     monitorTab : true,
21571     // private
21572     deferHeight : true,
21573
21574     
21575     actionMode : 'wrap',
21576     // private
21577     onResize : function(w, h){
21578         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21579         if(typeof w == 'number'){
21580             var x = w - this.trigger.getWidth();
21581             this.el.setWidth(this.adjustWidth('input', x));
21582             this.trigger.setStyle('left', x+'px');
21583         }
21584     },
21585
21586     // private
21587     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21588
21589     // private
21590     getResizeEl : function(){
21591         return this.wrap;
21592     },
21593
21594     // private
21595     getPositionEl : function(){
21596         return this.wrap;
21597     },
21598
21599     // private
21600     alignErrorIcon : function(){
21601         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21602     },
21603
21604     // private
21605     onRender : function(ct, position){
21606         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21607         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21608         this.trigger = this.wrap.createChild(this.triggerConfig ||
21609                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21610         if(this.hideTrigger){
21611             this.trigger.setDisplayed(false);
21612         }
21613         this.initTrigger();
21614         if(!this.width){
21615             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21616         }
21617     },
21618
21619     // private
21620     initTrigger : function(){
21621         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21622         this.trigger.addClassOnOver('x-form-trigger-over');
21623         this.trigger.addClassOnClick('x-form-trigger-click');
21624     },
21625
21626     // private
21627     onDestroy : function(){
21628         if(this.trigger){
21629             this.trigger.removeAllListeners();
21630             this.trigger.remove();
21631         }
21632         if(this.wrap){
21633             this.wrap.remove();
21634         }
21635         Roo.form.TriggerField.superclass.onDestroy.call(this);
21636     },
21637
21638     // private
21639     onFocus : function(){
21640         Roo.form.TriggerField.superclass.onFocus.call(this);
21641         if(!this.mimicing){
21642             this.wrap.addClass('x-trigger-wrap-focus');
21643             this.mimicing = true;
21644             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21645             if(this.monitorTab){
21646                 this.el.on("keydown", this.checkTab, this);
21647             }
21648         }
21649     },
21650
21651     // private
21652     checkTab : function(e){
21653         if(e.getKey() == e.TAB){
21654             this.triggerBlur();
21655         }
21656     },
21657
21658     // private
21659     onBlur : function(){
21660         // do nothing
21661     },
21662
21663     // private
21664     mimicBlur : function(e, t){
21665         if(!this.wrap.contains(t) && this.validateBlur()){
21666             this.triggerBlur();
21667         }
21668     },
21669
21670     // private
21671     triggerBlur : function(){
21672         this.mimicing = false;
21673         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21674         if(this.monitorTab){
21675             this.el.un("keydown", this.checkTab, this);
21676         }
21677         this.wrap.removeClass('x-trigger-wrap-focus');
21678         Roo.form.TriggerField.superclass.onBlur.call(this);
21679     },
21680
21681     // private
21682     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21683     validateBlur : function(e, t){
21684         return true;
21685     },
21686
21687     // private
21688     onDisable : function(){
21689         Roo.form.TriggerField.superclass.onDisable.call(this);
21690         if(this.wrap){
21691             this.wrap.addClass('x-item-disabled');
21692         }
21693     },
21694
21695     // private
21696     onEnable : function(){
21697         Roo.form.TriggerField.superclass.onEnable.call(this);
21698         if(this.wrap){
21699             this.wrap.removeClass('x-item-disabled');
21700         }
21701     },
21702
21703     // private
21704     onShow : function(){
21705         var ae = this.getActionEl();
21706         
21707         if(ae){
21708             ae.dom.style.display = '';
21709             ae.dom.style.visibility = 'visible';
21710         }
21711     },
21712
21713     // private
21714     
21715     onHide : function(){
21716         var ae = this.getActionEl();
21717         ae.dom.style.display = 'none';
21718     },
21719
21720     /**
21721      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
21722      * by an implementing function.
21723      * @method
21724      * @param {EventObject} e
21725      */
21726     onTriggerClick : Roo.emptyFn
21727 });
21728
21729 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
21730 // to be extended by an implementing class.  For an example of implementing this class, see the custom
21731 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21732 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21733     initComponent : function(){
21734         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21735
21736         this.triggerConfig = {
21737             tag:'span', cls:'x-form-twin-triggers', cn:[
21738             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21739             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21740         ]};
21741     },
21742
21743     getTrigger : function(index){
21744         return this.triggers[index];
21745     },
21746
21747     initTrigger : function(){
21748         var ts = this.trigger.select('.x-form-trigger', true);
21749         this.wrap.setStyle('overflow', 'hidden');
21750         var triggerField = this;
21751         ts.each(function(t, all, index){
21752             t.hide = function(){
21753                 var w = triggerField.wrap.getWidth();
21754                 this.dom.style.display = 'none';
21755                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21756             };
21757             t.show = function(){
21758                 var w = triggerField.wrap.getWidth();
21759                 this.dom.style.display = '';
21760                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21761             };
21762             var triggerIndex = 'Trigger'+(index+1);
21763
21764             if(this['hide'+triggerIndex]){
21765                 t.dom.style.display = 'none';
21766             }
21767             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
21768             t.addClassOnOver('x-form-trigger-over');
21769             t.addClassOnClick('x-form-trigger-click');
21770         }, this);
21771         this.triggers = ts.elements;
21772     },
21773
21774     onTrigger1Click : Roo.emptyFn,
21775     onTrigger2Click : Roo.emptyFn
21776 });/*
21777  * Based on:
21778  * Ext JS Library 1.1.1
21779  * Copyright(c) 2006-2007, Ext JS, LLC.
21780  *
21781  * Originally Released Under LGPL - original licence link has changed is not relivant.
21782  *
21783  * Fork - LGPL
21784  * <script type="text/javascript">
21785  */
21786  
21787 /**
21788  * @class Roo.form.TextArea
21789  * @extends Roo.form.TextField
21790  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
21791  * support for auto-sizing.
21792  * @constructor
21793  * Creates a new TextArea
21794  * @param {Object} config Configuration options
21795  */
21796 Roo.form.TextArea = function(config){
21797     Roo.form.TextArea.superclass.constructor.call(this, config);
21798     // these are provided exchanges for backwards compat
21799     // minHeight/maxHeight were replaced by growMin/growMax to be
21800     // compatible with TextField growing config values
21801     if(this.minHeight !== undefined){
21802         this.growMin = this.minHeight;
21803     }
21804     if(this.maxHeight !== undefined){
21805         this.growMax = this.maxHeight;
21806     }
21807 };
21808
21809 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
21810     /**
21811      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
21812      */
21813     growMin : 60,
21814     /**
21815      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
21816      */
21817     growMax: 1000,
21818     /**
21819      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
21820      * in the field (equivalent to setting overflow: hidden, defaults to false)
21821      */
21822     preventScrollbars: false,
21823     /**
21824      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21825      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
21826      */
21827
21828     // private
21829     onRender : function(ct, position){
21830         if(!this.el){
21831             this.defaultAutoCreate = {
21832                 tag: "textarea",
21833                 style:"width:300px;height:60px;",
21834                 autocomplete: "new-password"
21835             };
21836         }
21837         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
21838         if(this.grow){
21839             this.textSizeEl = Roo.DomHelper.append(document.body, {
21840                 tag: "pre", cls: "x-form-grow-sizer"
21841             });
21842             if(this.preventScrollbars){
21843                 this.el.setStyle("overflow", "hidden");
21844             }
21845             this.el.setHeight(this.growMin);
21846         }
21847     },
21848
21849     onDestroy : function(){
21850         if(this.textSizeEl){
21851             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
21852         }
21853         Roo.form.TextArea.superclass.onDestroy.call(this);
21854     },
21855
21856     // private
21857     onKeyUp : function(e){
21858         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
21859             this.autoSize();
21860         }
21861     },
21862
21863     /**
21864      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
21865      * This only takes effect if grow = true, and fires the autosize event if the height changes.
21866      */
21867     autoSize : function(){
21868         if(!this.grow || !this.textSizeEl){
21869             return;
21870         }
21871         var el = this.el;
21872         var v = el.dom.value;
21873         var ts = this.textSizeEl;
21874
21875         ts.innerHTML = '';
21876         ts.appendChild(document.createTextNode(v));
21877         v = ts.innerHTML;
21878
21879         Roo.fly(ts).setWidth(this.el.getWidth());
21880         if(v.length < 1){
21881             v = "&#160;&#160;";
21882         }else{
21883             if(Roo.isIE){
21884                 v = v.replace(/\n/g, '<p>&#160;</p>');
21885             }
21886             v += "&#160;\n&#160;";
21887         }
21888         ts.innerHTML = v;
21889         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
21890         if(h != this.lastHeight){
21891             this.lastHeight = h;
21892             this.el.setHeight(h);
21893             this.fireEvent("autosize", this, h);
21894         }
21895     }
21896 });/*
21897  * Based on:
21898  * Ext JS Library 1.1.1
21899  * Copyright(c) 2006-2007, Ext JS, LLC.
21900  *
21901  * Originally Released Under LGPL - original licence link has changed is not relivant.
21902  *
21903  * Fork - LGPL
21904  * <script type="text/javascript">
21905  */
21906  
21907
21908 /**
21909  * @class Roo.form.NumberField
21910  * @extends Roo.form.TextField
21911  * Numeric text field that provides automatic keystroke filtering and numeric validation.
21912  * @constructor
21913  * Creates a new NumberField
21914  * @param {Object} config Configuration options
21915  */
21916 Roo.form.NumberField = function(config){
21917     Roo.form.NumberField.superclass.constructor.call(this, config);
21918 };
21919
21920 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
21921     /**
21922      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
21923      */
21924     fieldClass: "x-form-field x-form-num-field",
21925     /**
21926      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
21927      */
21928     allowDecimals : true,
21929     /**
21930      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
21931      */
21932     decimalSeparator : ".",
21933     /**
21934      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
21935      */
21936     decimalPrecision : 2,
21937     /**
21938      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
21939      */
21940     allowNegative : true,
21941     /**
21942      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
21943      */
21944     minValue : Number.NEGATIVE_INFINITY,
21945     /**
21946      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
21947      */
21948     maxValue : Number.MAX_VALUE,
21949     /**
21950      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
21951      */
21952     minText : "The minimum value for this field is {0}",
21953     /**
21954      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
21955      */
21956     maxText : "The maximum value for this field is {0}",
21957     /**
21958      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
21959      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
21960      */
21961     nanText : "{0} is not a valid number",
21962
21963     // private
21964     initEvents : function(){
21965         Roo.form.NumberField.superclass.initEvents.call(this);
21966         var allowed = "0123456789";
21967         if(this.allowDecimals){
21968             allowed += this.decimalSeparator;
21969         }
21970         if(this.allowNegative){
21971             allowed += "-";
21972         }
21973         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
21974         var keyPress = function(e){
21975             var k = e.getKey();
21976             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
21977                 return;
21978             }
21979             var c = e.getCharCode();
21980             if(allowed.indexOf(String.fromCharCode(c)) === -1){
21981                 e.stopEvent();
21982             }
21983         };
21984         this.el.on("keypress", keyPress, this);
21985     },
21986
21987     // private
21988     validateValue : function(value){
21989         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
21990             return false;
21991         }
21992         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
21993              return true;
21994         }
21995         var num = this.parseValue(value);
21996         if(isNaN(num)){
21997             this.markInvalid(String.format(this.nanText, value));
21998             return false;
21999         }
22000         if(num < this.minValue){
22001             this.markInvalid(String.format(this.minText, this.minValue));
22002             return false;
22003         }
22004         if(num > this.maxValue){
22005             this.markInvalid(String.format(this.maxText, this.maxValue));
22006             return false;
22007         }
22008         return true;
22009     },
22010
22011     getValue : function(){
22012         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22013     },
22014
22015     // private
22016     parseValue : function(value){
22017         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22018         return isNaN(value) ? '' : value;
22019     },
22020
22021     // private
22022     fixPrecision : function(value){
22023         var nan = isNaN(value);
22024         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22025             return nan ? '' : value;
22026         }
22027         return parseFloat(value).toFixed(this.decimalPrecision);
22028     },
22029
22030     setValue : function(v){
22031         v = this.fixPrecision(v);
22032         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22033     },
22034
22035     // private
22036     decimalPrecisionFcn : function(v){
22037         return Math.floor(v);
22038     },
22039
22040     beforeBlur : function(){
22041         var v = this.parseValue(this.getRawValue());
22042         if(v){
22043             this.setValue(v);
22044         }
22045     }
22046 });/*
22047  * Based on:
22048  * Ext JS Library 1.1.1
22049  * Copyright(c) 2006-2007, Ext JS, LLC.
22050  *
22051  * Originally Released Under LGPL - original licence link has changed is not relivant.
22052  *
22053  * Fork - LGPL
22054  * <script type="text/javascript">
22055  */
22056  
22057 /**
22058  * @class Roo.form.DateField
22059  * @extends Roo.form.TriggerField
22060  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22061 * @constructor
22062 * Create a new DateField
22063 * @param {Object} config
22064  */
22065 Roo.form.DateField = function(config){
22066     Roo.form.DateField.superclass.constructor.call(this, config);
22067     
22068       this.addEvents({
22069          
22070         /**
22071          * @event select
22072          * Fires when a date is selected
22073              * @param {Roo.form.DateField} combo This combo box
22074              * @param {Date} date The date selected
22075              */
22076         'select' : true
22077          
22078     });
22079     
22080     
22081     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22082     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22083     this.ddMatch = null;
22084     if(this.disabledDates){
22085         var dd = this.disabledDates;
22086         var re = "(?:";
22087         for(var i = 0; i < dd.length; i++){
22088             re += dd[i];
22089             if(i != dd.length-1) re += "|";
22090         }
22091         this.ddMatch = new RegExp(re + ")");
22092     }
22093 };
22094
22095 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22096     /**
22097      * @cfg {String} format
22098      * The default date format string which can be overriden for localization support.  The format must be
22099      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22100      */
22101     format : "m/d/y",
22102     /**
22103      * @cfg {String} altFormats
22104      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22105      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22106      */
22107     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22108     /**
22109      * @cfg {Array} disabledDays
22110      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22111      */
22112     disabledDays : null,
22113     /**
22114      * @cfg {String} disabledDaysText
22115      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22116      */
22117     disabledDaysText : "Disabled",
22118     /**
22119      * @cfg {Array} disabledDates
22120      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22121      * expression so they are very powerful. Some examples:
22122      * <ul>
22123      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22124      * <li>["03/08", "09/16"] would disable those days for every year</li>
22125      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22126      * <li>["03/../2006"] would disable every day in March 2006</li>
22127      * <li>["^03"] would disable every day in every March</li>
22128      * </ul>
22129      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22130      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22131      */
22132     disabledDates : null,
22133     /**
22134      * @cfg {String} disabledDatesText
22135      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22136      */
22137     disabledDatesText : "Disabled",
22138     /**
22139      * @cfg {Date/String} minValue
22140      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22141      * valid format (defaults to null).
22142      */
22143     minValue : null,
22144     /**
22145      * @cfg {Date/String} maxValue
22146      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22147      * valid format (defaults to null).
22148      */
22149     maxValue : null,
22150     /**
22151      * @cfg {String} minText
22152      * The error text to display when the date in the cell is before minValue (defaults to
22153      * 'The date in this field must be after {minValue}').
22154      */
22155     minText : "The date in this field must be equal to or after {0}",
22156     /**
22157      * @cfg {String} maxText
22158      * The error text to display when the date in the cell is after maxValue (defaults to
22159      * 'The date in this field must be before {maxValue}').
22160      */
22161     maxText : "The date in this field must be equal to or before {0}",
22162     /**
22163      * @cfg {String} invalidText
22164      * The error text to display when the date in the field is invalid (defaults to
22165      * '{value} is not a valid date - it must be in the format {format}').
22166      */
22167     invalidText : "{0} is not a valid date - it must be in the format {1}",
22168     /**
22169      * @cfg {String} triggerClass
22170      * An additional CSS class used to style the trigger button.  The trigger will always get the
22171      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22172      * which displays a calendar icon).
22173      */
22174     triggerClass : 'x-form-date-trigger',
22175     
22176
22177     /**
22178      * @cfg {Boolean} useIso
22179      * if enabled, then the date field will use a hidden field to store the 
22180      * real value as iso formated date. default (false)
22181      */ 
22182     useIso : false,
22183     /**
22184      * @cfg {String/Object} autoCreate
22185      * A DomHelper element spec, or true for a default element spec (defaults to
22186      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22187      */ 
22188     // private
22189     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22190     
22191     // private
22192     hiddenField: false,
22193     
22194     onRender : function(ct, position)
22195     {
22196         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22197         if (this.useIso) {
22198             //this.el.dom.removeAttribute('name'); 
22199             Roo.log("Changing name?");
22200             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
22201             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22202                     'before', true);
22203             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22204             // prevent input submission
22205             this.hiddenName = this.name;
22206         }
22207             
22208             
22209     },
22210     
22211     // private
22212     validateValue : function(value)
22213     {
22214         value = this.formatDate(value);
22215         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22216             Roo.log('super failed');
22217             return false;
22218         }
22219         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22220              return true;
22221         }
22222         var svalue = value;
22223         value = this.parseDate(value);
22224         if(!value){
22225             Roo.log('parse date failed' + svalue);
22226             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22227             return false;
22228         }
22229         var time = value.getTime();
22230         if(this.minValue && time < this.minValue.getTime()){
22231             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22232             return false;
22233         }
22234         if(this.maxValue && time > this.maxValue.getTime()){
22235             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22236             return false;
22237         }
22238         if(this.disabledDays){
22239             var day = value.getDay();
22240             for(var i = 0; i < this.disabledDays.length; i++) {
22241                 if(day === this.disabledDays[i]){
22242                     this.markInvalid(this.disabledDaysText);
22243                     return false;
22244                 }
22245             }
22246         }
22247         var fvalue = this.formatDate(value);
22248         if(this.ddMatch && this.ddMatch.test(fvalue)){
22249             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22250             return false;
22251         }
22252         return true;
22253     },
22254
22255     // private
22256     // Provides logic to override the default TriggerField.validateBlur which just returns true
22257     validateBlur : function(){
22258         return !this.menu || !this.menu.isVisible();
22259     },
22260     
22261     getName: function()
22262     {
22263         // returns hidden if it's set..
22264         if (!this.rendered) {return ''};
22265         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
22266         
22267     },
22268
22269     /**
22270      * Returns the current date value of the date field.
22271      * @return {Date} The date value
22272      */
22273     getValue : function(){
22274         
22275         return  this.hiddenField ?
22276                 this.hiddenField.value :
22277                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22278     },
22279
22280     /**
22281      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22282      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22283      * (the default format used is "m/d/y").
22284      * <br />Usage:
22285      * <pre><code>
22286 //All of these calls set the same date value (May 4, 2006)
22287
22288 //Pass a date object:
22289 var dt = new Date('5/4/06');
22290 dateField.setValue(dt);
22291
22292 //Pass a date string (default format):
22293 dateField.setValue('5/4/06');
22294
22295 //Pass a date string (custom format):
22296 dateField.format = 'Y-m-d';
22297 dateField.setValue('2006-5-4');
22298 </code></pre>
22299      * @param {String/Date} date The date or valid date string
22300      */
22301     setValue : function(date){
22302         if (this.hiddenField) {
22303             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22304         }
22305         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22306         // make sure the value field is always stored as a date..
22307         this.value = this.parseDate(date);
22308         
22309         
22310     },
22311
22312     // private
22313     parseDate : function(value){
22314         if(!value || value instanceof Date){
22315             return value;
22316         }
22317         var v = Date.parseDate(value, this.format);
22318          if (!v && this.useIso) {
22319             v = Date.parseDate(value, 'Y-m-d');
22320         }
22321         if(!v && this.altFormats){
22322             if(!this.altFormatsArray){
22323                 this.altFormatsArray = this.altFormats.split("|");
22324             }
22325             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22326                 v = Date.parseDate(value, this.altFormatsArray[i]);
22327             }
22328         }
22329         return v;
22330     },
22331
22332     // private
22333     formatDate : function(date, fmt){
22334         return (!date || !(date instanceof Date)) ?
22335                date : date.dateFormat(fmt || this.format);
22336     },
22337
22338     // private
22339     menuListeners : {
22340         select: function(m, d){
22341             
22342             this.setValue(d);
22343             this.fireEvent('select', this, d);
22344         },
22345         show : function(){ // retain focus styling
22346             this.onFocus();
22347         },
22348         hide : function(){
22349             this.focus.defer(10, this);
22350             var ml = this.menuListeners;
22351             this.menu.un("select", ml.select,  this);
22352             this.menu.un("show", ml.show,  this);
22353             this.menu.un("hide", ml.hide,  this);
22354         }
22355     },
22356
22357     // private
22358     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22359     onTriggerClick : function(){
22360         if(this.disabled){
22361             return;
22362         }
22363         if(this.menu == null){
22364             this.menu = new Roo.menu.DateMenu();
22365         }
22366         Roo.apply(this.menu.picker,  {
22367             showClear: this.allowBlank,
22368             minDate : this.minValue,
22369             maxDate : this.maxValue,
22370             disabledDatesRE : this.ddMatch,
22371             disabledDatesText : this.disabledDatesText,
22372             disabledDays : this.disabledDays,
22373             disabledDaysText : this.disabledDaysText,
22374             format : this.useIso ? 'Y-m-d' : this.format,
22375             minText : String.format(this.minText, this.formatDate(this.minValue)),
22376             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22377         });
22378         this.menu.on(Roo.apply({}, this.menuListeners, {
22379             scope:this
22380         }));
22381         this.menu.picker.setValue(this.getValue() || new Date());
22382         this.menu.show(this.el, "tl-bl?");
22383     },
22384
22385     beforeBlur : function(){
22386         var v = this.parseDate(this.getRawValue());
22387         if(v){
22388             this.setValue(v);
22389         }
22390     },
22391
22392     /*@
22393      * overide
22394      * 
22395      */
22396     isDirty : function() {
22397         if(this.disabled) {
22398             return false;
22399         }
22400         
22401         if(typeof(this.startValue) === 'undefined'){
22402             return false;
22403         }
22404         
22405         return String(this.getValue()) !== String(this.startValue);
22406         
22407     }
22408 });/*
22409  * Based on:
22410  * Ext JS Library 1.1.1
22411  * Copyright(c) 2006-2007, Ext JS, LLC.
22412  *
22413  * Originally Released Under LGPL - original licence link has changed is not relivant.
22414  *
22415  * Fork - LGPL
22416  * <script type="text/javascript">
22417  */
22418  
22419 /**
22420  * @class Roo.form.MonthField
22421  * @extends Roo.form.TriggerField
22422  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22423 * @constructor
22424 * Create a new MonthField
22425 * @param {Object} config
22426  */
22427 Roo.form.MonthField = function(config){
22428     
22429     Roo.form.MonthField.superclass.constructor.call(this, config);
22430     
22431       this.addEvents({
22432          
22433         /**
22434          * @event select
22435          * Fires when a date is selected
22436              * @param {Roo.form.MonthFieeld} combo This combo box
22437              * @param {Date} date The date selected
22438              */
22439         'select' : true
22440          
22441     });
22442     
22443     
22444     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22445     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22446     this.ddMatch = null;
22447     if(this.disabledDates){
22448         var dd = this.disabledDates;
22449         var re = "(?:";
22450         for(var i = 0; i < dd.length; i++){
22451             re += dd[i];
22452             if(i != dd.length-1) re += "|";
22453         }
22454         this.ddMatch = new RegExp(re + ")");
22455     }
22456 };
22457
22458 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
22459     /**
22460      * @cfg {String} format
22461      * The default date format string which can be overriden for localization support.  The format must be
22462      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22463      */
22464     format : "M Y",
22465     /**
22466      * @cfg {String} altFormats
22467      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22468      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22469      */
22470     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
22471     /**
22472      * @cfg {Array} disabledDays
22473      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22474      */
22475     disabledDays : [0,1,2,3,4,5,6],
22476     /**
22477      * @cfg {String} disabledDaysText
22478      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22479      */
22480     disabledDaysText : "Disabled",
22481     /**
22482      * @cfg {Array} disabledDates
22483      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22484      * expression so they are very powerful. Some examples:
22485      * <ul>
22486      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22487      * <li>["03/08", "09/16"] would disable those days for every year</li>
22488      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22489      * <li>["03/../2006"] would disable every day in March 2006</li>
22490      * <li>["^03"] would disable every day in every March</li>
22491      * </ul>
22492      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22493      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22494      */
22495     disabledDates : null,
22496     /**
22497      * @cfg {String} disabledDatesText
22498      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22499      */
22500     disabledDatesText : "Disabled",
22501     /**
22502      * @cfg {Date/String} minValue
22503      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22504      * valid format (defaults to null).
22505      */
22506     minValue : null,
22507     /**
22508      * @cfg {Date/String} maxValue
22509      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22510      * valid format (defaults to null).
22511      */
22512     maxValue : null,
22513     /**
22514      * @cfg {String} minText
22515      * The error text to display when the date in the cell is before minValue (defaults to
22516      * 'The date in this field must be after {minValue}').
22517      */
22518     minText : "The date in this field must be equal to or after {0}",
22519     /**
22520      * @cfg {String} maxTextf
22521      * The error text to display when the date in the cell is after maxValue (defaults to
22522      * 'The date in this field must be before {maxValue}').
22523      */
22524     maxText : "The date in this field must be equal to or before {0}",
22525     /**
22526      * @cfg {String} invalidText
22527      * The error text to display when the date in the field is invalid (defaults to
22528      * '{value} is not a valid date - it must be in the format {format}').
22529      */
22530     invalidText : "{0} is not a valid date - it must be in the format {1}",
22531     /**
22532      * @cfg {String} triggerClass
22533      * An additional CSS class used to style the trigger button.  The trigger will always get the
22534      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22535      * which displays a calendar icon).
22536      */
22537     triggerClass : 'x-form-date-trigger',
22538     
22539
22540     /**
22541      * @cfg {Boolean} useIso
22542      * if enabled, then the date field will use a hidden field to store the 
22543      * real value as iso formated date. default (true)
22544      */ 
22545     useIso : true,
22546     /**
22547      * @cfg {String/Object} autoCreate
22548      * A DomHelper element spec, or true for a default element spec (defaults to
22549      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22550      */ 
22551     // private
22552     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
22553     
22554     // private
22555     hiddenField: false,
22556     
22557     hideMonthPicker : false,
22558     
22559     onRender : function(ct, position)
22560     {
22561         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
22562         if (this.useIso) {
22563             this.el.dom.removeAttribute('name'); 
22564             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22565                     'before', true);
22566             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22567             // prevent input submission
22568             this.hiddenName = this.name;
22569         }
22570             
22571             
22572     },
22573     
22574     // private
22575     validateValue : function(value)
22576     {
22577         value = this.formatDate(value);
22578         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
22579             return false;
22580         }
22581         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22582              return true;
22583         }
22584         var svalue = value;
22585         value = this.parseDate(value);
22586         if(!value){
22587             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22588             return false;
22589         }
22590         var time = value.getTime();
22591         if(this.minValue && time < this.minValue.getTime()){
22592             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22593             return false;
22594         }
22595         if(this.maxValue && time > this.maxValue.getTime()){
22596             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22597             return false;
22598         }
22599         /*if(this.disabledDays){
22600             var day = value.getDay();
22601             for(var i = 0; i < this.disabledDays.length; i++) {
22602                 if(day === this.disabledDays[i]){
22603                     this.markInvalid(this.disabledDaysText);
22604                     return false;
22605                 }
22606             }
22607         }
22608         */
22609         var fvalue = this.formatDate(value);
22610         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
22611             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22612             return false;
22613         }
22614         */
22615         return true;
22616     },
22617
22618     // private
22619     // Provides logic to override the default TriggerField.validateBlur which just returns true
22620     validateBlur : function(){
22621         return !this.menu || !this.menu.isVisible();
22622     },
22623
22624     /**
22625      * Returns the current date value of the date field.
22626      * @return {Date} The date value
22627      */
22628     getValue : function(){
22629         
22630         
22631         
22632         return  this.hiddenField ?
22633                 this.hiddenField.value :
22634                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
22635     },
22636
22637     /**
22638      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22639      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
22640      * (the default format used is "m/d/y").
22641      * <br />Usage:
22642      * <pre><code>
22643 //All of these calls set the same date value (May 4, 2006)
22644
22645 //Pass a date object:
22646 var dt = new Date('5/4/06');
22647 monthField.setValue(dt);
22648
22649 //Pass a date string (default format):
22650 monthField.setValue('5/4/06');
22651
22652 //Pass a date string (custom format):
22653 monthField.format = 'Y-m-d';
22654 monthField.setValue('2006-5-4');
22655 </code></pre>
22656      * @param {String/Date} date The date or valid date string
22657      */
22658     setValue : function(date){
22659         Roo.log('month setValue' + date);
22660         // can only be first of month..
22661         
22662         var val = this.parseDate(date);
22663         
22664         if (this.hiddenField) {
22665             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22666         }
22667         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22668         this.value = this.parseDate(date);
22669     },
22670
22671     // private
22672     parseDate : function(value){
22673         if(!value || value instanceof Date){
22674             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
22675             return value;
22676         }
22677         var v = Date.parseDate(value, this.format);
22678         if (!v && this.useIso) {
22679             v = Date.parseDate(value, 'Y-m-d');
22680         }
22681         if (v) {
22682             // 
22683             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
22684         }
22685         
22686         
22687         if(!v && this.altFormats){
22688             if(!this.altFormatsArray){
22689                 this.altFormatsArray = this.altFormats.split("|");
22690             }
22691             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22692                 v = Date.parseDate(value, this.altFormatsArray[i]);
22693             }
22694         }
22695         return v;
22696     },
22697
22698     // private
22699     formatDate : function(date, fmt){
22700         return (!date || !(date instanceof Date)) ?
22701                date : date.dateFormat(fmt || this.format);
22702     },
22703
22704     // private
22705     menuListeners : {
22706         select: function(m, d){
22707             this.setValue(d);
22708             this.fireEvent('select', this, d);
22709         },
22710         show : function(){ // retain focus styling
22711             this.onFocus();
22712         },
22713         hide : function(){
22714             this.focus.defer(10, this);
22715             var ml = this.menuListeners;
22716             this.menu.un("select", ml.select,  this);
22717             this.menu.un("show", ml.show,  this);
22718             this.menu.un("hide", ml.hide,  this);
22719         }
22720     },
22721     // private
22722     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22723     onTriggerClick : function(){
22724         if(this.disabled){
22725             return;
22726         }
22727         if(this.menu == null){
22728             this.menu = new Roo.menu.DateMenu();
22729            
22730         }
22731         
22732         Roo.apply(this.menu.picker,  {
22733             
22734             showClear: this.allowBlank,
22735             minDate : this.minValue,
22736             maxDate : this.maxValue,
22737             disabledDatesRE : this.ddMatch,
22738             disabledDatesText : this.disabledDatesText,
22739             
22740             format : this.useIso ? 'Y-m-d' : this.format,
22741             minText : String.format(this.minText, this.formatDate(this.minValue)),
22742             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22743             
22744         });
22745          this.menu.on(Roo.apply({}, this.menuListeners, {
22746             scope:this
22747         }));
22748        
22749         
22750         var m = this.menu;
22751         var p = m.picker;
22752         
22753         // hide month picker get's called when we called by 'before hide';
22754         
22755         var ignorehide = true;
22756         p.hideMonthPicker  = function(disableAnim){
22757             if (ignorehide) {
22758                 return;
22759             }
22760              if(this.monthPicker){
22761                 Roo.log("hideMonthPicker called");
22762                 if(disableAnim === true){
22763                     this.monthPicker.hide();
22764                 }else{
22765                     this.monthPicker.slideOut('t', {duration:.2});
22766                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
22767                     p.fireEvent("select", this, this.value);
22768                     m.hide();
22769                 }
22770             }
22771         }
22772         
22773         Roo.log('picker set value');
22774         Roo.log(this.getValue());
22775         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
22776         m.show(this.el, 'tl-bl?');
22777         ignorehide  = false;
22778         // this will trigger hideMonthPicker..
22779         
22780         
22781         // hidden the day picker
22782         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
22783         
22784         
22785         
22786       
22787         
22788         p.showMonthPicker.defer(100, p);
22789     
22790         
22791        
22792     },
22793
22794     beforeBlur : function(){
22795         var v = this.parseDate(this.getRawValue());
22796         if(v){
22797             this.setValue(v);
22798         }
22799     }
22800
22801     /** @cfg {Boolean} grow @hide */
22802     /** @cfg {Number} growMin @hide */
22803     /** @cfg {Number} growMax @hide */
22804     /**
22805      * @hide
22806      * @method autoSize
22807      */
22808 });/*
22809  * Based on:
22810  * Ext JS Library 1.1.1
22811  * Copyright(c) 2006-2007, Ext JS, LLC.
22812  *
22813  * Originally Released Under LGPL - original licence link has changed is not relivant.
22814  *
22815  * Fork - LGPL
22816  * <script type="text/javascript">
22817  */
22818  
22819
22820 /**
22821  * @class Roo.form.ComboBox
22822  * @extends Roo.form.TriggerField
22823  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22824  * @constructor
22825  * Create a new ComboBox.
22826  * @param {Object} config Configuration options
22827  */
22828 Roo.form.ComboBox = function(config){
22829     Roo.form.ComboBox.superclass.constructor.call(this, config);
22830     this.addEvents({
22831         /**
22832          * @event expand
22833          * Fires when the dropdown list is expanded
22834              * @param {Roo.form.ComboBox} combo This combo box
22835              */
22836         'expand' : true,
22837         /**
22838          * @event collapse
22839          * Fires when the dropdown list is collapsed
22840              * @param {Roo.form.ComboBox} combo This combo box
22841              */
22842         'collapse' : true,
22843         /**
22844          * @event beforeselect
22845          * Fires before a list item is selected. Return false to cancel the selection.
22846              * @param {Roo.form.ComboBox} combo This combo box
22847              * @param {Roo.data.Record} record The data record returned from the underlying store
22848              * @param {Number} index The index of the selected item in the dropdown list
22849              */
22850         'beforeselect' : true,
22851         /**
22852          * @event select
22853          * Fires when a list item is selected
22854              * @param {Roo.form.ComboBox} combo This combo box
22855              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22856              * @param {Number} index The index of the selected item in the dropdown list
22857              */
22858         'select' : true,
22859         /**
22860          * @event beforequery
22861          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22862          * The event object passed has these properties:
22863              * @param {Roo.form.ComboBox} combo This combo box
22864              * @param {String} query The query
22865              * @param {Boolean} forceAll true to force "all" query
22866              * @param {Boolean} cancel true to cancel the query
22867              * @param {Object} e The query event object
22868              */
22869         'beforequery': true,
22870          /**
22871          * @event add
22872          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22873              * @param {Roo.form.ComboBox} combo This combo box
22874              */
22875         'add' : true,
22876         /**
22877          * @event edit
22878          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22879              * @param {Roo.form.ComboBox} combo This combo box
22880              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22881              */
22882         'edit' : true
22883         
22884         
22885     });
22886     if(this.transform){
22887         this.allowDomMove = false;
22888         var s = Roo.getDom(this.transform);
22889         if(!this.hiddenName){
22890             this.hiddenName = s.name;
22891         }
22892         if(!this.store){
22893             this.mode = 'local';
22894             var d = [], opts = s.options;
22895             for(var i = 0, len = opts.length;i < len; i++){
22896                 var o = opts[i];
22897                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22898                 if(o.selected) {
22899                     this.value = value;
22900                 }
22901                 d.push([value, o.text]);
22902             }
22903             this.store = new Roo.data.SimpleStore({
22904                 'id': 0,
22905                 fields: ['value', 'text'],
22906                 data : d
22907             });
22908             this.valueField = 'value';
22909             this.displayField = 'text';
22910         }
22911         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22912         if(!this.lazyRender){
22913             this.target = true;
22914             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22915             s.parentNode.removeChild(s); // remove it
22916             this.render(this.el.parentNode);
22917         }else{
22918             s.parentNode.removeChild(s); // remove it
22919         }
22920
22921     }
22922     if (this.store) {
22923         this.store = Roo.factory(this.store, Roo.data);
22924     }
22925     
22926     this.selectedIndex = -1;
22927     if(this.mode == 'local'){
22928         if(config.queryDelay === undefined){
22929             this.queryDelay = 10;
22930         }
22931         if(config.minChars === undefined){
22932             this.minChars = 0;
22933         }
22934     }
22935 };
22936
22937 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22938     /**
22939      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22940      */
22941     /**
22942      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22943      * rendering into an Roo.Editor, defaults to false)
22944      */
22945     /**
22946      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22947      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22948      */
22949     /**
22950      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22951      */
22952     /**
22953      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22954      * the dropdown list (defaults to undefined, with no header element)
22955      */
22956
22957      /**
22958      * @cfg {String/Roo.Template} tpl The template to use to render the output
22959      */
22960      
22961     // private
22962     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22963     /**
22964      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22965      */
22966     listWidth: undefined,
22967     /**
22968      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22969      * mode = 'remote' or 'text' if mode = 'local')
22970      */
22971     displayField: undefined,
22972     /**
22973      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22974      * mode = 'remote' or 'value' if mode = 'local'). 
22975      * Note: use of a valueField requires the user make a selection
22976      * in order for a value to be mapped.
22977      */
22978     valueField: undefined,
22979     
22980     
22981     /**
22982      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22983      * field's data value (defaults to the underlying DOM element's name)
22984      */
22985     hiddenName: undefined,
22986     /**
22987      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22988      */
22989     listClass: '',
22990     /**
22991      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22992      */
22993     selectedClass: 'x-combo-selected',
22994     /**
22995      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22996      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22997      * which displays a downward arrow icon).
22998      */
22999     triggerClass : 'x-form-arrow-trigger',
23000     /**
23001      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
23002      */
23003     shadow:'sides',
23004     /**
23005      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
23006      * anchor positions (defaults to 'tl-bl')
23007      */
23008     listAlign: 'tl-bl?',
23009     /**
23010      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
23011      */
23012     maxHeight: 300,
23013     /**
23014      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
23015      * query specified by the allQuery config option (defaults to 'query')
23016      */
23017     triggerAction: 'query',
23018     /**
23019      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
23020      * (defaults to 4, does not apply if editable = false)
23021      */
23022     minChars : 4,
23023     /**
23024      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
23025      * delay (typeAheadDelay) if it matches a known value (defaults to false)
23026      */
23027     typeAhead: false,
23028     /**
23029      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23030      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23031      */
23032     queryDelay: 500,
23033     /**
23034      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23035      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
23036      */
23037     pageSize: 0,
23038     /**
23039      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23040      * when editable = true (defaults to false)
23041      */
23042     selectOnFocus:false,
23043     /**
23044      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23045      */
23046     queryParam: 'query',
23047     /**
23048      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23049      * when mode = 'remote' (defaults to 'Loading...')
23050      */
23051     loadingText: 'Loading...',
23052     /**
23053      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23054      */
23055     resizable: false,
23056     /**
23057      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23058      */
23059     handleHeight : 8,
23060     /**
23061      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23062      * traditional select (defaults to true)
23063      */
23064     editable: true,
23065     /**
23066      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23067      */
23068     allQuery: '',
23069     /**
23070      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23071      */
23072     mode: 'remote',
23073     /**
23074      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23075      * listWidth has a higher value)
23076      */
23077     minListWidth : 70,
23078     /**
23079      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23080      * allow the user to set arbitrary text into the field (defaults to false)
23081      */
23082     forceSelection:false,
23083     /**
23084      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23085      * if typeAhead = true (defaults to 250)
23086      */
23087     typeAheadDelay : 250,
23088     /**
23089      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23090      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23091      */
23092     valueNotFoundText : undefined,
23093     /**
23094      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23095      */
23096     blockFocus : false,
23097     
23098     /**
23099      * @cfg {Boolean} disableClear Disable showing of clear button.
23100      */
23101     disableClear : false,
23102     /**
23103      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23104      */
23105     alwaysQuery : false,
23106     
23107     //private
23108     addicon : false,
23109     editicon: false,
23110     
23111     // element that contains real text value.. (when hidden is used..)
23112      
23113     // private
23114     onRender : function(ct, position){
23115         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23116         if(this.hiddenName){
23117             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23118                     'before', true);
23119             this.hiddenField.value =
23120                 this.hiddenValue !== undefined ? this.hiddenValue :
23121                 this.value !== undefined ? this.value : '';
23122
23123             // prevent input submission
23124             this.el.dom.removeAttribute('name');
23125              
23126              
23127         }
23128         if(Roo.isGecko){
23129             this.el.dom.setAttribute('autocomplete', 'off');
23130         }
23131
23132         var cls = 'x-combo-list';
23133
23134         this.list = new Roo.Layer({
23135             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23136         });
23137
23138         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23139         this.list.setWidth(lw);
23140         this.list.swallowEvent('mousewheel');
23141         this.assetHeight = 0;
23142
23143         if(this.title){
23144             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23145             this.assetHeight += this.header.getHeight();
23146         }
23147
23148         this.innerList = this.list.createChild({cls:cls+'-inner'});
23149         this.innerList.on('mouseover', this.onViewOver, this);
23150         this.innerList.on('mousemove', this.onViewMove, this);
23151         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23152         
23153         if(this.allowBlank && !this.pageSize && !this.disableClear){
23154             this.footer = this.list.createChild({cls:cls+'-ft'});
23155             this.pageTb = new Roo.Toolbar(this.footer);
23156            
23157         }
23158         if(this.pageSize){
23159             this.footer = this.list.createChild({cls:cls+'-ft'});
23160             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23161                     {pageSize: this.pageSize});
23162             
23163         }
23164         
23165         if (this.pageTb && this.allowBlank && !this.disableClear) {
23166             var _this = this;
23167             this.pageTb.add(new Roo.Toolbar.Fill(), {
23168                 cls: 'x-btn-icon x-btn-clear',
23169                 text: '&#160;',
23170                 handler: function()
23171                 {
23172                     _this.collapse();
23173                     _this.clearValue();
23174                     _this.onSelect(false, -1);
23175                 }
23176             });
23177         }
23178         if (this.footer) {
23179             this.assetHeight += this.footer.getHeight();
23180         }
23181         
23182
23183         if(!this.tpl){
23184             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23185         }
23186
23187         this.view = new Roo.View(this.innerList, this.tpl, {
23188             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23189         });
23190
23191         this.view.on('click', this.onViewClick, this);
23192
23193         this.store.on('beforeload', this.onBeforeLoad, this);
23194         this.store.on('load', this.onLoad, this);
23195         this.store.on('loadexception', this.onLoadException, this);
23196
23197         if(this.resizable){
23198             this.resizer = new Roo.Resizable(this.list,  {
23199                pinned:true, handles:'se'
23200             });
23201             this.resizer.on('resize', function(r, w, h){
23202                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23203                 this.listWidth = w;
23204                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23205                 this.restrictHeight();
23206             }, this);
23207             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23208         }
23209         if(!this.editable){
23210             this.editable = true;
23211             this.setEditable(false);
23212         }  
23213         
23214         
23215         if (typeof(this.events.add.listeners) != 'undefined') {
23216             
23217             this.addicon = this.wrap.createChild(
23218                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23219        
23220             this.addicon.on('click', function(e) {
23221                 this.fireEvent('add', this);
23222             }, this);
23223         }
23224         if (typeof(this.events.edit.listeners) != 'undefined') {
23225             
23226             this.editicon = this.wrap.createChild(
23227                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23228             if (this.addicon) {
23229                 this.editicon.setStyle('margin-left', '40px');
23230             }
23231             this.editicon.on('click', function(e) {
23232                 
23233                 // we fire even  if inothing is selected..
23234                 this.fireEvent('edit', this, this.lastData );
23235                 
23236             }, this);
23237         }
23238         
23239         
23240         
23241     },
23242
23243     // private
23244     initEvents : function(){
23245         Roo.form.ComboBox.superclass.initEvents.call(this);
23246
23247         this.keyNav = new Roo.KeyNav(this.el, {
23248             "up" : function(e){
23249                 this.inKeyMode = true;
23250                 this.selectPrev();
23251             },
23252
23253             "down" : function(e){
23254                 if(!this.isExpanded()){
23255                     this.onTriggerClick();
23256                 }else{
23257                     this.inKeyMode = true;
23258                     this.selectNext();
23259                 }
23260             },
23261
23262             "enter" : function(e){
23263                 this.onViewClick();
23264                 //return true;
23265             },
23266
23267             "esc" : function(e){
23268                 this.collapse();
23269             },
23270
23271             "tab" : function(e){
23272                 this.onViewClick(false);
23273                 this.fireEvent("specialkey", this, e);
23274                 return true;
23275             },
23276
23277             scope : this,
23278
23279             doRelay : function(foo, bar, hname){
23280                 if(hname == 'down' || this.scope.isExpanded()){
23281                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23282                 }
23283                 return true;
23284             },
23285
23286             forceKeyDown: true
23287         });
23288         this.queryDelay = Math.max(this.queryDelay || 10,
23289                 this.mode == 'local' ? 10 : 250);
23290         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23291         if(this.typeAhead){
23292             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23293         }
23294         if(this.editable !== false){
23295             this.el.on("keyup", this.onKeyUp, this);
23296         }
23297         if(this.forceSelection){
23298             this.on('blur', this.doForce, this);
23299         }
23300     },
23301
23302     onDestroy : function(){
23303         if(this.view){
23304             this.view.setStore(null);
23305             this.view.el.removeAllListeners();
23306             this.view.el.remove();
23307             this.view.purgeListeners();
23308         }
23309         if(this.list){
23310             this.list.destroy();
23311         }
23312         if(this.store){
23313             this.store.un('beforeload', this.onBeforeLoad, this);
23314             this.store.un('load', this.onLoad, this);
23315             this.store.un('loadexception', this.onLoadException, this);
23316         }
23317         Roo.form.ComboBox.superclass.onDestroy.call(this);
23318     },
23319
23320     // private
23321     fireKey : function(e){
23322         if(e.isNavKeyPress() && !this.list.isVisible()){
23323             this.fireEvent("specialkey", this, e);
23324         }
23325     },
23326
23327     // private
23328     onResize: function(w, h){
23329         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23330         
23331         if(typeof w != 'number'){
23332             // we do not handle it!?!?
23333             return;
23334         }
23335         var tw = this.trigger.getWidth();
23336         tw += this.addicon ? this.addicon.getWidth() : 0;
23337         tw += this.editicon ? this.editicon.getWidth() : 0;
23338         var x = w - tw;
23339         this.el.setWidth( this.adjustWidth('input', x));
23340             
23341         this.trigger.setStyle('left', x+'px');
23342         
23343         if(this.list && this.listWidth === undefined){
23344             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23345             this.list.setWidth(lw);
23346             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23347         }
23348         
23349     
23350         
23351     },
23352
23353     /**
23354      * Allow or prevent the user from directly editing the field text.  If false is passed,
23355      * the user will only be able to select from the items defined in the dropdown list.  This method
23356      * is the runtime equivalent of setting the 'editable' config option at config time.
23357      * @param {Boolean} value True to allow the user to directly edit the field text
23358      */
23359     setEditable : function(value){
23360         if(value == this.editable){
23361             return;
23362         }
23363         this.editable = value;
23364         if(!value){
23365             this.el.dom.setAttribute('readOnly', true);
23366             this.el.on('mousedown', this.onTriggerClick,  this);
23367             this.el.addClass('x-combo-noedit');
23368         }else{
23369             this.el.dom.setAttribute('readOnly', false);
23370             this.el.un('mousedown', this.onTriggerClick,  this);
23371             this.el.removeClass('x-combo-noedit');
23372         }
23373     },
23374
23375     // private
23376     onBeforeLoad : function(){
23377         if(!this.hasFocus){
23378             return;
23379         }
23380         this.innerList.update(this.loadingText ?
23381                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23382         this.restrictHeight();
23383         this.selectedIndex = -1;
23384     },
23385
23386     // private
23387     onLoad : function(){
23388         if(!this.hasFocus){
23389             return;
23390         }
23391         if(this.store.getCount() > 0){
23392             this.expand();
23393             this.restrictHeight();
23394             if(this.lastQuery == this.allQuery){
23395                 if(this.editable){
23396                     this.el.dom.select();
23397                 }
23398                 if(!this.selectByValue(this.value, true)){
23399                     this.select(0, true);
23400                 }
23401             }else{
23402                 this.selectNext();
23403                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23404                     this.taTask.delay(this.typeAheadDelay);
23405                 }
23406             }
23407         }else{
23408             this.onEmptyResults();
23409         }
23410         //this.el.focus();
23411     },
23412     // private
23413     onLoadException : function()
23414     {
23415         this.collapse();
23416         Roo.log(this.store.reader.jsonData);
23417         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
23418             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
23419         }
23420         
23421         
23422     },
23423     // private
23424     onTypeAhead : function(){
23425         if(this.store.getCount() > 0){
23426             var r = this.store.getAt(0);
23427             var newValue = r.data[this.displayField];
23428             var len = newValue.length;
23429             var selStart = this.getRawValue().length;
23430             if(selStart != len){
23431                 this.setRawValue(newValue);
23432                 this.selectText(selStart, newValue.length);
23433             }
23434         }
23435     },
23436
23437     // private
23438     onSelect : function(record, index){
23439         if(this.fireEvent('beforeselect', this, record, index) !== false){
23440             this.setFromData(index > -1 ? record.data : false);
23441             this.collapse();
23442             this.fireEvent('select', this, record, index);
23443         }
23444     },
23445
23446     /**
23447      * Returns the currently selected field value or empty string if no value is set.
23448      * @return {String} value The selected value
23449      */
23450     getValue : function(){
23451         if(this.valueField){
23452             return typeof this.value != 'undefined' ? this.value : '';
23453         }
23454         return Roo.form.ComboBox.superclass.getValue.call(this);
23455     },
23456
23457     /**
23458      * Clears any text/value currently set in the field
23459      */
23460     clearValue : function(){
23461         if(this.hiddenField){
23462             this.hiddenField.value = '';
23463         }
23464         this.value = '';
23465         this.setRawValue('');
23466         this.lastSelectionText = '';
23467         
23468     },
23469
23470     /**
23471      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23472      * will be displayed in the field.  If the value does not match the data value of an existing item,
23473      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23474      * Otherwise the field will be blank (although the value will still be set).
23475      * @param {String} value The value to match
23476      */
23477     setValue : function(v){
23478         var text = v;
23479         if(this.valueField){
23480             var r = this.findRecord(this.valueField, v);
23481             if(r){
23482                 text = r.data[this.displayField];
23483             }else if(this.valueNotFoundText !== undefined){
23484                 text = this.valueNotFoundText;
23485             }
23486         }
23487         this.lastSelectionText = text;
23488         if(this.hiddenField){
23489             this.hiddenField.value = v;
23490         }
23491         Roo.form.ComboBox.superclass.setValue.call(this, text);
23492         this.value = v;
23493     },
23494     /**
23495      * @property {Object} the last set data for the element
23496      */
23497     
23498     lastData : false,
23499     /**
23500      * Sets the value of the field based on a object which is related to the record format for the store.
23501      * @param {Object} value the value to set as. or false on reset?
23502      */
23503     setFromData : function(o){
23504         var dv = ''; // display value
23505         var vv = ''; // value value..
23506         this.lastData = o;
23507         if (this.displayField) {
23508             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23509         } else {
23510             // this is an error condition!!!
23511             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23512         }
23513         
23514         if(this.valueField){
23515             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23516         }
23517         if(this.hiddenField){
23518             this.hiddenField.value = vv;
23519             
23520             this.lastSelectionText = dv;
23521             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23522             this.value = vv;
23523             return;
23524         }
23525         // no hidden field.. - we store the value in 'value', but still display
23526         // display field!!!!
23527         this.lastSelectionText = dv;
23528         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23529         this.value = vv;
23530         
23531         
23532     },
23533     // private
23534     reset : function(){
23535         // overridden so that last data is reset..
23536         this.setValue(this.resetValue);
23537         this.clearInvalid();
23538         this.lastData = false;
23539         if (this.view) {
23540             this.view.clearSelections();
23541         }
23542     },
23543     // private
23544     findRecord : function(prop, value){
23545         var record;
23546         if(this.store.getCount() > 0){
23547             this.store.each(function(r){
23548                 if(r.data[prop] == value){
23549                     record = r;
23550                     return false;
23551                 }
23552                 return true;
23553             });
23554         }
23555         return record;
23556     },
23557     
23558     getName: function()
23559     {
23560         // returns hidden if it's set..
23561         if (!this.rendered) {return ''};
23562         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
23563         
23564     },
23565     // private
23566     onViewMove : function(e, t){
23567         this.inKeyMode = false;
23568     },
23569
23570     // private
23571     onViewOver : function(e, t){
23572         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23573             return;
23574         }
23575         var item = this.view.findItemFromChild(t);
23576         if(item){
23577             var index = this.view.indexOf(item);
23578             this.select(index, false);
23579         }
23580     },
23581
23582     // private
23583     onViewClick : function(doFocus)
23584     {
23585         var index = this.view.getSelectedIndexes()[0];
23586         var r = this.store.getAt(index);
23587         if(r){
23588             this.onSelect(r, index);
23589         }
23590         if(doFocus !== false && !this.blockFocus){
23591             this.el.focus();
23592         }
23593     },
23594
23595     // private
23596     restrictHeight : function(){
23597         this.innerList.dom.style.height = '';
23598         var inner = this.innerList.dom;
23599         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23600         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23601         this.list.beginUpdate();
23602         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23603         this.list.alignTo(this.el, this.listAlign);
23604         this.list.endUpdate();
23605     },
23606
23607     // private
23608     onEmptyResults : function(){
23609         this.collapse();
23610     },
23611
23612     /**
23613      * Returns true if the dropdown list is expanded, else false.
23614      */
23615     isExpanded : function(){
23616         return this.list.isVisible();
23617     },
23618
23619     /**
23620      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23621      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23622      * @param {String} value The data value of the item to select
23623      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23624      * selected item if it is not currently in view (defaults to true)
23625      * @return {Boolean} True if the value matched an item in the list, else false
23626      */
23627     selectByValue : function(v, scrollIntoView){
23628         if(v !== undefined && v !== null){
23629             var r = this.findRecord(this.valueField || this.displayField, v);
23630             if(r){
23631                 this.select(this.store.indexOf(r), scrollIntoView);
23632                 return true;
23633             }
23634         }
23635         return false;
23636     },
23637
23638     /**
23639      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23640      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23641      * @param {Number} index The zero-based index of the list item to select
23642      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23643      * selected item if it is not currently in view (defaults to true)
23644      */
23645     select : function(index, scrollIntoView){
23646         this.selectedIndex = index;
23647         this.view.select(index);
23648         if(scrollIntoView !== false){
23649             var el = this.view.getNode(index);
23650             if(el){
23651                 this.innerList.scrollChildIntoView(el, false);
23652             }
23653         }
23654     },
23655
23656     // private
23657     selectNext : function(){
23658         var ct = this.store.getCount();
23659         if(ct > 0){
23660             if(this.selectedIndex == -1){
23661                 this.select(0);
23662             }else if(this.selectedIndex < ct-1){
23663                 this.select(this.selectedIndex+1);
23664             }
23665         }
23666     },
23667
23668     // private
23669     selectPrev : function(){
23670         var ct = this.store.getCount();
23671         if(ct > 0){
23672             if(this.selectedIndex == -1){
23673                 this.select(0);
23674             }else if(this.selectedIndex != 0){
23675                 this.select(this.selectedIndex-1);
23676             }
23677         }
23678     },
23679
23680     // private
23681     onKeyUp : function(e){
23682         if(this.editable !== false && !e.isSpecialKey()){
23683             this.lastKey = e.getKey();
23684             this.dqTask.delay(this.queryDelay);
23685         }
23686     },
23687
23688     // private
23689     validateBlur : function(){
23690         return !this.list || !this.list.isVisible();   
23691     },
23692
23693     // private
23694     initQuery : function(){
23695         this.doQuery(this.getRawValue());
23696     },
23697
23698     // private
23699     doForce : function(){
23700         if(this.el.dom.value.length > 0){
23701             this.el.dom.value =
23702                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23703              
23704         }
23705     },
23706
23707     /**
23708      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23709      * query allowing the query action to be canceled if needed.
23710      * @param {String} query The SQL query to execute
23711      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23712      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23713      * saved in the current store (defaults to false)
23714      */
23715     doQuery : function(q, forceAll){
23716         if(q === undefined || q === null){
23717             q = '';
23718         }
23719         var qe = {
23720             query: q,
23721             forceAll: forceAll,
23722             combo: this,
23723             cancel:false
23724         };
23725         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23726             return false;
23727         }
23728         q = qe.query;
23729         forceAll = qe.forceAll;
23730         if(forceAll === true || (q.length >= this.minChars)){
23731             if(this.lastQuery != q || this.alwaysQuery){
23732                 this.lastQuery = q;
23733                 if(this.mode == 'local'){
23734                     this.selectedIndex = -1;
23735                     if(forceAll){
23736                         this.store.clearFilter();
23737                     }else{
23738                         this.store.filter(this.displayField, q);
23739                     }
23740                     this.onLoad();
23741                 }else{
23742                     this.store.baseParams[this.queryParam] = q;
23743                     this.store.load({
23744                         params: this.getParams(q)
23745                     });
23746                     this.expand();
23747                 }
23748             }else{
23749                 this.selectedIndex = -1;
23750                 this.onLoad();   
23751             }
23752         }
23753     },
23754
23755     // private
23756     getParams : function(q){
23757         var p = {};
23758         //p[this.queryParam] = q;
23759         if(this.pageSize){
23760             p.start = 0;
23761             p.limit = this.pageSize;
23762         }
23763         return p;
23764     },
23765
23766     /**
23767      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23768      */
23769     collapse : function(){
23770         if(!this.isExpanded()){
23771             return;
23772         }
23773         this.list.hide();
23774         Roo.get(document).un('mousedown', this.collapseIf, this);
23775         Roo.get(document).un('mousewheel', this.collapseIf, this);
23776         if (!this.editable) {
23777             Roo.get(document).un('keydown', this.listKeyPress, this);
23778         }
23779         this.fireEvent('collapse', this);
23780     },
23781
23782     // private
23783     collapseIf : function(e){
23784         if(!e.within(this.wrap) && !e.within(this.list)){
23785             this.collapse();
23786         }
23787     },
23788
23789     /**
23790      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23791      */
23792     expand : function(){
23793         if(this.isExpanded() || !this.hasFocus){
23794             return;
23795         }
23796         this.list.alignTo(this.el, this.listAlign);
23797         this.list.show();
23798         Roo.get(document).on('mousedown', this.collapseIf, this);
23799         Roo.get(document).on('mousewheel', this.collapseIf, this);
23800         if (!this.editable) {
23801             Roo.get(document).on('keydown', this.listKeyPress, this);
23802         }
23803         
23804         this.fireEvent('expand', this);
23805     },
23806
23807     // private
23808     // Implements the default empty TriggerField.onTriggerClick function
23809     onTriggerClick : function(){
23810         if(this.disabled){
23811             return;
23812         }
23813         if(this.isExpanded()){
23814             this.collapse();
23815             if (!this.blockFocus) {
23816                 this.el.focus();
23817             }
23818             
23819         }else {
23820             this.hasFocus = true;
23821             if(this.triggerAction == 'all') {
23822                 this.doQuery(this.allQuery, true);
23823             } else {
23824                 this.doQuery(this.getRawValue());
23825             }
23826             if (!this.blockFocus) {
23827                 this.el.focus();
23828             }
23829         }
23830     },
23831     listKeyPress : function(e)
23832     {
23833         //Roo.log('listkeypress');
23834         // scroll to first matching element based on key pres..
23835         if (e.isSpecialKey()) {
23836             return false;
23837         }
23838         var k = String.fromCharCode(e.getKey()).toUpperCase();
23839         //Roo.log(k);
23840         var match  = false;
23841         var csel = this.view.getSelectedNodes();
23842         var cselitem = false;
23843         if (csel.length) {
23844             var ix = this.view.indexOf(csel[0]);
23845             cselitem  = this.store.getAt(ix);
23846             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23847                 cselitem = false;
23848             }
23849             
23850         }
23851         
23852         this.store.each(function(v) { 
23853             if (cselitem) {
23854                 // start at existing selection.
23855                 if (cselitem.id == v.id) {
23856                     cselitem = false;
23857                 }
23858                 return;
23859             }
23860                 
23861             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23862                 match = this.store.indexOf(v);
23863                 return false;
23864             }
23865         }, this);
23866         
23867         if (match === false) {
23868             return true; // no more action?
23869         }
23870         // scroll to?
23871         this.view.select(match);
23872         var sn = Roo.get(this.view.getSelectedNodes()[0])
23873         sn.scrollIntoView(sn.dom.parentNode, false);
23874     }
23875
23876     /** 
23877     * @cfg {Boolean} grow 
23878     * @hide 
23879     */
23880     /** 
23881     * @cfg {Number} growMin 
23882     * @hide 
23883     */
23884     /** 
23885     * @cfg {Number} growMax 
23886     * @hide 
23887     */
23888     /**
23889      * @hide
23890      * @method autoSize
23891      */
23892 });/*
23893  * Copyright(c) 2010-2012, Roo J Solutions Limited
23894  *
23895  * Licence LGPL
23896  *
23897  */
23898
23899 /**
23900  * @class Roo.form.ComboBoxArray
23901  * @extends Roo.form.TextField
23902  * A facebook style adder... for lists of email / people / countries  etc...
23903  * pick multiple items from a combo box, and shows each one.
23904  *
23905  *  Fred [x]  Brian [x]  [Pick another |v]
23906  *
23907  *
23908  *  For this to work: it needs various extra information
23909  *    - normal combo problay has
23910  *      name, hiddenName
23911  *    + displayField, valueField
23912  *
23913  *    For our purpose...
23914  *
23915  *
23916  *   If we change from 'extends' to wrapping...
23917  *   
23918  *  
23919  *
23920  
23921  
23922  * @constructor
23923  * Create a new ComboBoxArray.
23924  * @param {Object} config Configuration options
23925  */
23926  
23927
23928 Roo.form.ComboBoxArray = function(config)
23929 {
23930     this.addEvents({
23931         /**
23932          * @event beforeremove
23933          * Fires before remove the value from the list
23934              * @param {Roo.form.ComboBoxArray} _self This combo box array
23935              * @param {Roo.form.ComboBoxArray.Item} item removed item
23936              */
23937         'beforeremove' : true,
23938         /**
23939          * @event remove
23940          * Fires when remove the value from the list
23941              * @param {Roo.form.ComboBoxArray} _self This combo box array
23942              * @param {Roo.form.ComboBoxArray.Item} item removed item
23943              */
23944         'remove' : true
23945         
23946         
23947     });
23948     
23949     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
23950     
23951     this.items = new Roo.util.MixedCollection(false);
23952     
23953     // construct the child combo...
23954     
23955     
23956     
23957     
23958    
23959     
23960 }
23961
23962  
23963 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
23964
23965     /**
23966      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
23967      */
23968     
23969     lastData : false,
23970     
23971     // behavies liek a hiddne field
23972     inputType:      'hidden',
23973     /**
23974      * @cfg {Number} width The width of the box that displays the selected element
23975      */ 
23976     width:          300,
23977
23978     
23979     
23980     /**
23981      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
23982      */
23983     name : false,
23984     /**
23985      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
23986      */
23987     hiddenName : false,
23988     
23989     
23990     // private the array of items that are displayed..
23991     items  : false,
23992     // private - the hidden field el.
23993     hiddenEl : false,
23994     // private - the filed el..
23995     el : false,
23996     
23997     //validateValue : function() { return true; }, // all values are ok!
23998     //onAddClick: function() { },
23999     
24000     onRender : function(ct, position) 
24001     {
24002         
24003         // create the standard hidden element
24004         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
24005         
24006         
24007         // give fake names to child combo;
24008         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
24009         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
24010         
24011         this.combo = Roo.factory(this.combo, Roo.form);
24012         this.combo.onRender(ct, position);
24013         if (typeof(this.combo.width) != 'undefined') {
24014             this.combo.onResize(this.combo.width,0);
24015         }
24016         
24017         this.combo.initEvents();
24018         
24019         // assigned so form know we need to do this..
24020         this.store          = this.combo.store;
24021         this.valueField     = this.combo.valueField;
24022         this.displayField   = this.combo.displayField ;
24023         
24024         
24025         this.combo.wrap.addClass('x-cbarray-grp');
24026         
24027         var cbwrap = this.combo.wrap.createChild(
24028             {tag: 'div', cls: 'x-cbarray-cb'},
24029             this.combo.el.dom
24030         );
24031         
24032              
24033         this.hiddenEl = this.combo.wrap.createChild({
24034             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
24035         });
24036         this.el = this.combo.wrap.createChild({
24037             tag: 'input',  type:'hidden' , name: this.name, value : ''
24038         });
24039          //   this.el.dom.removeAttribute("name");
24040         
24041         
24042         this.outerWrap = this.combo.wrap;
24043         this.wrap = cbwrap;
24044         
24045         this.outerWrap.setWidth(this.width);
24046         this.outerWrap.dom.removeChild(this.el.dom);
24047         
24048         this.wrap.dom.appendChild(this.el.dom);
24049         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
24050         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
24051         
24052         this.combo.trigger.setStyle('position','relative');
24053         this.combo.trigger.setStyle('left', '0px');
24054         this.combo.trigger.setStyle('top', '2px');
24055         
24056         this.combo.el.setStyle('vertical-align', 'text-bottom');
24057         
24058         //this.trigger.setStyle('vertical-align', 'top');
24059         
24060         // this should use the code from combo really... on('add' ....)
24061         if (this.adder) {
24062             
24063         
24064             this.adder = this.outerWrap.createChild(
24065                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
24066             var _t = this;
24067             this.adder.on('click', function(e) {
24068                 _t.fireEvent('adderclick', this, e);
24069             }, _t);
24070         }
24071         //var _t = this;
24072         //this.adder.on('click', this.onAddClick, _t);
24073         
24074         
24075         this.combo.on('select', function(cb, rec, ix) {
24076             this.addItem(rec.data);
24077             
24078             cb.setValue('');
24079             cb.el.dom.value = '';
24080             //cb.lastData = rec.data;
24081             // add to list
24082             
24083         }, this);
24084         
24085         
24086     },
24087     
24088     
24089     getName: function()
24090     {
24091         // returns hidden if it's set..
24092         if (!this.rendered) {return ''};
24093         return  this.hiddenName ? this.hiddenName : this.name;
24094         
24095     },
24096     
24097     
24098     onResize: function(w, h){
24099         
24100         return;
24101         // not sure if this is needed..
24102         //this.combo.onResize(w,h);
24103         
24104         if(typeof w != 'number'){
24105             // we do not handle it!?!?
24106             return;
24107         }
24108         var tw = this.combo.trigger.getWidth();
24109         tw += this.addicon ? this.addicon.getWidth() : 0;
24110         tw += this.editicon ? this.editicon.getWidth() : 0;
24111         var x = w - tw;
24112         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24113             
24114         this.combo.trigger.setStyle('left', '0px');
24115         
24116         if(this.list && this.listWidth === undefined){
24117             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24118             this.list.setWidth(lw);
24119             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24120         }
24121         
24122     
24123         
24124     },
24125     
24126     addItem: function(rec)
24127     {
24128         var valueField = this.combo.valueField;
24129         var displayField = this.combo.displayField;
24130         if (this.items.indexOfKey(rec[valueField]) > -1) {
24131             //console.log("GOT " + rec.data.id);
24132             return;
24133         }
24134         
24135         var x = new Roo.form.ComboBoxArray.Item({
24136             //id : rec[this.idField],
24137             data : rec,
24138             displayField : displayField ,
24139             tipField : displayField ,
24140             cb : this
24141         });
24142         // use the 
24143         this.items.add(rec[valueField],x);
24144         // add it before the element..
24145         this.updateHiddenEl();
24146         x.render(this.outerWrap, this.wrap.dom);
24147         // add the image handler..
24148     },
24149     
24150     updateHiddenEl : function()
24151     {
24152         this.validate();
24153         if (!this.hiddenEl) {
24154             return;
24155         }
24156         var ar = [];
24157         var idField = this.combo.valueField;
24158         
24159         this.items.each(function(f) {
24160             ar.push(f.data[idField]);
24161            
24162         });
24163         this.hiddenEl.dom.value = ar.join(',');
24164         this.validate();
24165     },
24166     
24167     reset : function()
24168     {
24169         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
24170         this.items.each(function(f) {
24171            f.remove(); 
24172         });
24173         this.el.dom.value = '';
24174         if (this.hiddenEl) {
24175             this.hiddenEl.dom.value = '';
24176         }
24177         
24178     },
24179     getValue: function()
24180     {
24181         return this.hiddenEl ? this.hiddenEl.dom.value : '';
24182     },
24183     setValue: function(v) // not a valid action - must use addItems..
24184     {
24185          
24186         this.reset();
24187         
24188         
24189         
24190         if (this.store.isLocal && (typeof(v) == 'string')) {
24191             // then we can use the store to find the values..
24192             // comma seperated at present.. this needs to allow JSON based encoding..
24193             this.hiddenEl.value  = v;
24194             var v_ar = [];
24195             Roo.each(v.split(','), function(k) {
24196                 Roo.log("CHECK " + this.valueField + ',' + k);
24197                 var li = this.store.query(this.valueField, k);
24198                 if (!li.length) {
24199                     return;
24200                 }
24201                 var add = {};
24202                 add[this.valueField] = k;
24203                 add[this.displayField] = li.item(0).data[this.displayField];
24204                 
24205                 this.addItem(add);
24206             }, this) 
24207              
24208         }
24209         if (typeof(v) == 'object' ) {
24210             // then let's assume it's an array of objects..
24211             Roo.each(v, function(l) {
24212                 this.addItem(l);
24213             }, this);
24214              
24215         }
24216         
24217         
24218     },
24219     setFromData: function(v)
24220     {
24221         // this recieves an object, if setValues is called.
24222         this.reset();
24223         this.el.dom.value = v[this.displayField];
24224         this.hiddenEl.dom.value = v[this.valueField];
24225         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24226             return;
24227         }
24228         var kv = v[this.valueField];
24229         var dv = v[this.displayField];
24230         kv = typeof(kv) != 'string' ? '' : kv;
24231         dv = typeof(dv) != 'string' ? '' : dv;
24232         
24233         
24234         var keys = kv.split(',');
24235         var display = dv.split(',');
24236         for (var i = 0 ; i < keys.length; i++) {
24237             
24238             add = {};
24239             add[this.valueField] = keys[i];
24240             add[this.displayField] = display[i];
24241             this.addItem(add);
24242         }
24243       
24244         
24245     },
24246     
24247     /**
24248      * Validates the combox array value
24249      * @return {Boolean} True if the value is valid, else false
24250      */
24251     validate : function(){
24252         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
24253             this.clearInvalid();
24254             return true;
24255         }
24256         return false;
24257     },
24258     
24259     validateValue : function(value){
24260         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24261         
24262     },
24263     
24264     /*@
24265      * overide
24266      * 
24267      */
24268     isDirty : function() {
24269         if(this.disabled) {
24270             return false;
24271         }
24272         
24273         try {
24274             var d = Roo.decode(String(this.originalValue));
24275         } catch (e) {
24276             return String(this.getValue()) !== String(this.originalValue);
24277         }
24278         
24279         var originalValue = [];
24280         
24281         for (var i = 0; i < d.length; i++){
24282             originalValue.push(d[i][this.valueField]);
24283         }
24284         
24285         return String(this.getValue()) !== String(originalValue.join(','));
24286         
24287     }
24288     
24289 });
24290
24291
24292
24293 /**
24294  * @class Roo.form.ComboBoxArray.Item
24295  * @extends Roo.BoxComponent
24296  * A selected item in the list
24297  *  Fred [x]  Brian [x]  [Pick another |v]
24298  * 
24299  * @constructor
24300  * Create a new item.
24301  * @param {Object} config Configuration options
24302  */
24303  
24304 Roo.form.ComboBoxArray.Item = function(config) {
24305     config.id = Roo.id();
24306     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24307 }
24308
24309 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24310     data : {},
24311     cb: false,
24312     displayField : false,
24313     tipField : false,
24314     
24315     
24316     defaultAutoCreate : {
24317         tag: 'div',
24318         cls: 'x-cbarray-item',
24319         cn : [ 
24320             { tag: 'div' },
24321             {
24322                 tag: 'img',
24323                 width:16,
24324                 height : 16,
24325                 src : Roo.BLANK_IMAGE_URL ,
24326                 align: 'center'
24327             }
24328         ]
24329         
24330     },
24331     
24332  
24333     onRender : function(ct, position)
24334     {
24335         Roo.form.Field.superclass.onRender.call(this, ct, position);
24336         
24337         if(!this.el){
24338             var cfg = this.getAutoCreate();
24339             this.el = ct.createChild(cfg, position);
24340         }
24341         
24342         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24343         
24344         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
24345             this.cb.renderer(this.data) :
24346             String.format('{0}',this.data[this.displayField]);
24347         
24348             
24349         this.el.child('div').dom.setAttribute('qtip',
24350                         String.format('{0}',this.data[this.tipField])
24351         );
24352         
24353         this.el.child('img').on('click', this.remove, this);
24354         
24355     },
24356    
24357     remove : function()
24358     {
24359         if(this.cb.disabled){
24360             return;
24361         }
24362         
24363         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
24364             this.cb.items.remove(this);
24365             this.el.child('img').un('click', this.remove, this);
24366             this.el.remove();
24367             this.cb.updateHiddenEl();
24368
24369             this.cb.fireEvent('remove', this.cb, this);
24370         }
24371         
24372     }
24373 });/*
24374  * Based on:
24375  * Ext JS Library 1.1.1
24376  * Copyright(c) 2006-2007, Ext JS, LLC.
24377  *
24378  * Originally Released Under LGPL - original licence link has changed is not relivant.
24379  *
24380  * Fork - LGPL
24381  * <script type="text/javascript">
24382  */
24383 /**
24384  * @class Roo.form.Checkbox
24385  * @extends Roo.form.Field
24386  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
24387  * @constructor
24388  * Creates a new Checkbox
24389  * @param {Object} config Configuration options
24390  */
24391 Roo.form.Checkbox = function(config){
24392     Roo.form.Checkbox.superclass.constructor.call(this, config);
24393     this.addEvents({
24394         /**
24395          * @event check
24396          * Fires when the checkbox is checked or unchecked.
24397              * @param {Roo.form.Checkbox} this This checkbox
24398              * @param {Boolean} checked The new checked value
24399              */
24400         check : true
24401     });
24402 };
24403
24404 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
24405     /**
24406      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24407      */
24408     focusClass : undefined,
24409     /**
24410      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24411      */
24412     fieldClass: "x-form-field",
24413     /**
24414      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24415      */
24416     checked: false,
24417     /**
24418      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24419      * {tag: "input", type: "checkbox", autocomplete: "off"})
24420      */
24421     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
24422     /**
24423      * @cfg {String} boxLabel The text that appears beside the checkbox
24424      */
24425     boxLabel : "",
24426     /**
24427      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
24428      */  
24429     inputValue : '1',
24430     /**
24431      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24432      */
24433      valueOff: '0', // value when not checked..
24434
24435     actionMode : 'viewEl', 
24436     //
24437     // private
24438     itemCls : 'x-menu-check-item x-form-item',
24439     groupClass : 'x-menu-group-item',
24440     inputType : 'hidden',
24441     
24442     
24443     inSetChecked: false, // check that we are not calling self...
24444     
24445     inputElement: false, // real input element?
24446     basedOn: false, // ????
24447     
24448     isFormField: true, // not sure where this is needed!!!!
24449
24450     onResize : function(){
24451         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
24452         if(!this.boxLabel){
24453             this.el.alignTo(this.wrap, 'c-c');
24454         }
24455     },
24456
24457     initEvents : function(){
24458         Roo.form.Checkbox.superclass.initEvents.call(this);
24459         this.el.on("click", this.onClick,  this);
24460         this.el.on("change", this.onClick,  this);
24461     },
24462
24463
24464     getResizeEl : function(){
24465         return this.wrap;
24466     },
24467
24468     getPositionEl : function(){
24469         return this.wrap;
24470     },
24471
24472     // private
24473     onRender : function(ct, position){
24474         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24475         /*
24476         if(this.inputValue !== undefined){
24477             this.el.dom.value = this.inputValue;
24478         }
24479         */
24480         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24481         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24482         var viewEl = this.wrap.createChild({ 
24483             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24484         this.viewEl = viewEl;   
24485         this.wrap.on('click', this.onClick,  this); 
24486         
24487         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24488         this.el.on('propertychange', this.setFromHidden,  this);  //ie
24489         
24490         
24491         
24492         if(this.boxLabel){
24493             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24494         //    viewEl.on('click', this.onClick,  this); 
24495         }
24496         //if(this.checked){
24497             this.setChecked(this.checked);
24498         //}else{
24499             //this.checked = this.el.dom;
24500         //}
24501
24502     },
24503
24504     // private
24505     initValue : Roo.emptyFn,
24506
24507     /**
24508      * Returns the checked state of the checkbox.
24509      * @return {Boolean} True if checked, else false
24510      */
24511     getValue : function(){
24512         if(this.el){
24513             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
24514         }
24515         return this.valueOff;
24516         
24517     },
24518
24519         // private
24520     onClick : function(){ 
24521         if (this.disabled) {
24522             return;
24523         }
24524         this.setChecked(!this.checked);
24525
24526         //if(this.el.dom.checked != this.checked){
24527         //    this.setValue(this.el.dom.checked);
24528        // }
24529     },
24530
24531     /**
24532      * Sets the checked state of the checkbox.
24533      * On is always based on a string comparison between inputValue and the param.
24534      * @param {Boolean/String} value - the value to set 
24535      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
24536      */
24537     setValue : function(v,suppressEvent){
24538         
24539         
24540         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
24541         //if(this.el && this.el.dom){
24542         //    this.el.dom.checked = this.checked;
24543         //    this.el.dom.defaultChecked = this.checked;
24544         //}
24545         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
24546         //this.fireEvent("check", this, this.checked);
24547     },
24548     // private..
24549     setChecked : function(state,suppressEvent)
24550     {
24551         if (this.inSetChecked) {
24552             this.checked = state;
24553             return;
24554         }
24555         
24556     
24557         if(this.wrap){
24558             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
24559         }
24560         this.checked = state;
24561         if(suppressEvent !== true){
24562             this.fireEvent('check', this, state);
24563         }
24564         this.inSetChecked = true;
24565         this.el.dom.value = state ? this.inputValue : this.valueOff;
24566         this.inSetChecked = false;
24567         
24568     },
24569     // handle setting of hidden value by some other method!!?!?
24570     setFromHidden: function()
24571     {
24572         if(!this.el){
24573             return;
24574         }
24575         //console.log("SET FROM HIDDEN");
24576         //alert('setFrom hidden');
24577         this.setValue(this.el.dom.value);
24578     },
24579     
24580     onDestroy : function()
24581     {
24582         if(this.viewEl){
24583             Roo.get(this.viewEl).remove();
24584         }
24585          
24586         Roo.form.Checkbox.superclass.onDestroy.call(this);
24587     }
24588
24589 });/*
24590  * Based on:
24591  * Ext JS Library 1.1.1
24592  * Copyright(c) 2006-2007, Ext JS, LLC.
24593  *
24594  * Originally Released Under LGPL - original licence link has changed is not relivant.
24595  *
24596  * Fork - LGPL
24597  * <script type="text/javascript">
24598  */
24599  
24600 /**
24601  * @class Roo.form.Radio
24602  * @extends Roo.form.Checkbox
24603  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
24604  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
24605  * @constructor
24606  * Creates a new Radio
24607  * @param {Object} config Configuration options
24608  */
24609 Roo.form.Radio = function(){
24610     Roo.form.Radio.superclass.constructor.apply(this, arguments);
24611 };
24612 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
24613     inputType: 'radio',
24614
24615     /**
24616      * If this radio is part of a group, it will return the selected value
24617      * @return {String}
24618      */
24619     getGroupValue : function(){
24620         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
24621     },
24622     
24623     
24624     onRender : function(ct, position){
24625         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24626         
24627         if(this.inputValue !== undefined){
24628             this.el.dom.value = this.inputValue;
24629         }
24630          
24631         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24632         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24633         //var viewEl = this.wrap.createChild({ 
24634         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24635         //this.viewEl = viewEl;   
24636         //this.wrap.on('click', this.onClick,  this); 
24637         
24638         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24639         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
24640         
24641         
24642         
24643         if(this.boxLabel){
24644             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24645         //    viewEl.on('click', this.onClick,  this); 
24646         }
24647          if(this.checked){
24648             this.el.dom.checked =   'checked' ;
24649         }
24650          
24651     } 
24652     
24653     
24654 });//<script type="text/javascript">
24655
24656 /*
24657  * Based  Ext JS Library 1.1.1
24658  * Copyright(c) 2006-2007, Ext JS, LLC.
24659  * LGPL
24660  *
24661  */
24662  
24663 /**
24664  * @class Roo.HtmlEditorCore
24665  * @extends Roo.Component
24666  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24667  *
24668  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24669  */
24670
24671 Roo.HtmlEditorCore = function(config){
24672     
24673     
24674     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24675     
24676     
24677     this.addEvents({
24678         /**
24679          * @event initialize
24680          * Fires when the editor is fully initialized (including the iframe)
24681          * @param {Roo.HtmlEditorCore} this
24682          */
24683         initialize: true,
24684         /**
24685          * @event activate
24686          * Fires when the editor is first receives the focus. Any insertion must wait
24687          * until after this event.
24688          * @param {Roo.HtmlEditorCore} this
24689          */
24690         activate: true,
24691          /**
24692          * @event beforesync
24693          * Fires before the textarea is updated with content from the editor iframe. Return false
24694          * to cancel the sync.
24695          * @param {Roo.HtmlEditorCore} this
24696          * @param {String} html
24697          */
24698         beforesync: true,
24699          /**
24700          * @event beforepush
24701          * Fires before the iframe editor is updated with content from the textarea. Return false
24702          * to cancel the push.
24703          * @param {Roo.HtmlEditorCore} this
24704          * @param {String} html
24705          */
24706         beforepush: true,
24707          /**
24708          * @event sync
24709          * Fires when the textarea is updated with content from the editor iframe.
24710          * @param {Roo.HtmlEditorCore} this
24711          * @param {String} html
24712          */
24713         sync: true,
24714          /**
24715          * @event push
24716          * Fires when the iframe editor is updated with content from the textarea.
24717          * @param {Roo.HtmlEditorCore} this
24718          * @param {String} html
24719          */
24720         push: true,
24721         
24722         /**
24723          * @event editorevent
24724          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24725          * @param {Roo.HtmlEditorCore} this
24726          */
24727         editorevent: true
24728         
24729     });
24730     
24731     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24732     
24733     // defaults : white / black...
24734     this.applyBlacklists();
24735     
24736     
24737     
24738 };
24739
24740
24741 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24742
24743
24744      /**
24745      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24746      */
24747     
24748     owner : false,
24749     
24750      /**
24751      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24752      *                        Roo.resizable.
24753      */
24754     resizable : false,
24755      /**
24756      * @cfg {Number} height (in pixels)
24757      */   
24758     height: 300,
24759    /**
24760      * @cfg {Number} width (in pixels)
24761      */   
24762     width: 500,
24763     
24764     /**
24765      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24766      * 
24767      */
24768     stylesheets: false,
24769     
24770     // id of frame..
24771     frameId: false,
24772     
24773     // private properties
24774     validationEvent : false,
24775     deferHeight: true,
24776     initialized : false,
24777     activated : false,
24778     sourceEditMode : false,
24779     onFocus : Roo.emptyFn,
24780     iframePad:3,
24781     hideMode:'offsets',
24782     
24783     clearUp: true,
24784     
24785     // blacklist + whitelisted elements..
24786     black: false,
24787     white: false,
24788      
24789     
24790
24791     /**
24792      * Protected method that will not generally be called directly. It
24793      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24794      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24795      */
24796     getDocMarkup : function(){
24797         // body styles..
24798         var st = '';
24799         
24800         // inherit styels from page...?? 
24801         if (this.stylesheets === false) {
24802             
24803             Roo.get(document.head).select('style').each(function(node) {
24804                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24805             });
24806             
24807             Roo.get(document.head).select('link').each(function(node) { 
24808                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24809             });
24810             
24811         } else if (!this.stylesheets.length) {
24812                 // simple..
24813                 st = '<style type="text/css">' +
24814                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24815                    '</style>';
24816         } else { 
24817             
24818         }
24819         
24820         st +=  '<style type="text/css">' +
24821             'IMG { cursor: pointer } ' +
24822         '</style>';
24823
24824         
24825         return '<html><head>' + st  +
24826             //<style type="text/css">' +
24827             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24828             //'</style>' +
24829             ' </head><body class="roo-htmleditor-body"></body></html>';
24830     },
24831
24832     // private
24833     onRender : function(ct, position)
24834     {
24835         var _t = this;
24836         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24837         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24838         
24839         
24840         this.el.dom.style.border = '0 none';
24841         this.el.dom.setAttribute('tabIndex', -1);
24842         this.el.addClass('x-hidden hide');
24843         
24844         
24845         
24846         if(Roo.isIE){ // fix IE 1px bogus margin
24847             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24848         }
24849        
24850         
24851         this.frameId = Roo.id();
24852         
24853          
24854         
24855         var iframe = this.owner.wrap.createChild({
24856             tag: 'iframe',
24857             cls: 'form-control', // bootstrap..
24858             id: this.frameId,
24859             name: this.frameId,
24860             frameBorder : 'no',
24861             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24862         }, this.el
24863         );
24864         
24865         
24866         this.iframe = iframe.dom;
24867
24868          this.assignDocWin();
24869         
24870         this.doc.designMode = 'on';
24871        
24872         this.doc.open();
24873         this.doc.write(this.getDocMarkup());
24874         this.doc.close();
24875
24876         
24877         var task = { // must defer to wait for browser to be ready
24878             run : function(){
24879                 //console.log("run task?" + this.doc.readyState);
24880                 this.assignDocWin();
24881                 if(this.doc.body || this.doc.readyState == 'complete'){
24882                     try {
24883                         this.doc.designMode="on";
24884                     } catch (e) {
24885                         return;
24886                     }
24887                     Roo.TaskMgr.stop(task);
24888                     this.initEditor.defer(10, this);
24889                 }
24890             },
24891             interval : 10,
24892             duration: 10000,
24893             scope: this
24894         };
24895         Roo.TaskMgr.start(task);
24896
24897     },
24898
24899     // private
24900     onResize : function(w, h)
24901     {
24902          Roo.log('resize: ' +w + ',' + h );
24903         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24904         if(!this.iframe){
24905             return;
24906         }
24907         if(typeof w == 'number'){
24908             
24909             this.iframe.style.width = w + 'px';
24910         }
24911         if(typeof h == 'number'){
24912             
24913             this.iframe.style.height = h + 'px';
24914             if(this.doc){
24915                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24916             }
24917         }
24918         
24919     },
24920
24921     /**
24922      * Toggles the editor between standard and source edit mode.
24923      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24924      */
24925     toggleSourceEdit : function(sourceEditMode){
24926         
24927         this.sourceEditMode = sourceEditMode === true;
24928         
24929         if(this.sourceEditMode){
24930  
24931             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24932             
24933         }else{
24934             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24935             //this.iframe.className = '';
24936             this.deferFocus();
24937         }
24938         //this.setSize(this.owner.wrap.getSize());
24939         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24940     },
24941
24942     
24943   
24944
24945     /**
24946      * Protected method that will not generally be called directly. If you need/want
24947      * custom HTML cleanup, this is the method you should override.
24948      * @param {String} html The HTML to be cleaned
24949      * return {String} The cleaned HTML
24950      */
24951     cleanHtml : function(html){
24952         html = String(html);
24953         if(html.length > 5){
24954             if(Roo.isSafari){ // strip safari nonsense
24955                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24956             }
24957         }
24958         if(html == '&nbsp;'){
24959             html = '';
24960         }
24961         return html;
24962     },
24963
24964     /**
24965      * HTML Editor -> Textarea
24966      * Protected method that will not generally be called directly. Syncs the contents
24967      * of the editor iframe with the textarea.
24968      */
24969     syncValue : function(){
24970         if(this.initialized){
24971             var bd = (this.doc.body || this.doc.documentElement);
24972             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24973             var html = bd.innerHTML;
24974             if(Roo.isSafari){
24975                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24976                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24977                 if(m && m[1]){
24978                     html = '<div style="'+m[0]+'">' + html + '</div>';
24979                 }
24980             }
24981             html = this.cleanHtml(html);
24982             // fix up the special chars.. normaly like back quotes in word...
24983             // however we do not want to do this with chinese..
24984             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
24985                 var cc = b.charCodeAt();
24986                 if (
24987                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24988                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24989                     (cc >= 0xf900 && cc < 0xfb00 )
24990                 ) {
24991                         return b;
24992                 }
24993                 return "&#"+cc+";" 
24994             });
24995             if(this.owner.fireEvent('beforesync', this, html) !== false){
24996                 this.el.dom.value = html;
24997                 this.owner.fireEvent('sync', this, html);
24998             }
24999         }
25000     },
25001
25002     /**
25003      * Protected method that will not generally be called directly. Pushes the value of the textarea
25004      * into the iframe editor.
25005      */
25006     pushValue : function(){
25007         if(this.initialized){
25008             var v = this.el.dom.value.trim();
25009             
25010 //            if(v.length < 1){
25011 //                v = '&#160;';
25012 //            }
25013             
25014             if(this.owner.fireEvent('beforepush', this, v) !== false){
25015                 var d = (this.doc.body || this.doc.documentElement);
25016                 d.innerHTML = v;
25017                 this.cleanUpPaste();
25018                 this.el.dom.value = d.innerHTML;
25019                 this.owner.fireEvent('push', this, v);
25020             }
25021         }
25022     },
25023
25024     // private
25025     deferFocus : function(){
25026         this.focus.defer(10, this);
25027     },
25028
25029     // doc'ed in Field
25030     focus : function(){
25031         if(this.win && !this.sourceEditMode){
25032             this.win.focus();
25033         }else{
25034             this.el.focus();
25035         }
25036     },
25037     
25038     assignDocWin: function()
25039     {
25040         var iframe = this.iframe;
25041         
25042          if(Roo.isIE){
25043             this.doc = iframe.contentWindow.document;
25044             this.win = iframe.contentWindow;
25045         } else {
25046 //            if (!Roo.get(this.frameId)) {
25047 //                return;
25048 //            }
25049 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25050 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25051             
25052             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25053                 return;
25054             }
25055             
25056             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25057             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25058         }
25059     },
25060     
25061     // private
25062     initEditor : function(){
25063         //console.log("INIT EDITOR");
25064         this.assignDocWin();
25065         
25066         
25067         
25068         this.doc.designMode="on";
25069         this.doc.open();
25070         this.doc.write(this.getDocMarkup());
25071         this.doc.close();
25072         
25073         var dbody = (this.doc.body || this.doc.documentElement);
25074         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25075         // this copies styles from the containing element into thsi one..
25076         // not sure why we need all of this..
25077         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25078         
25079         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25080         //ss['background-attachment'] = 'fixed'; // w3c
25081         dbody.bgProperties = 'fixed'; // ie
25082         //Roo.DomHelper.applyStyles(dbody, ss);
25083         Roo.EventManager.on(this.doc, {
25084             //'mousedown': this.onEditorEvent,
25085             'mouseup': this.onEditorEvent,
25086             'dblclick': this.onEditorEvent,
25087             'click': this.onEditorEvent,
25088             'keyup': this.onEditorEvent,
25089             buffer:100,
25090             scope: this
25091         });
25092         if(Roo.isGecko){
25093             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25094         }
25095         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25096             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25097         }
25098         this.initialized = true;
25099
25100         this.owner.fireEvent('initialize', this);
25101         this.pushValue();
25102     },
25103
25104     // private
25105     onDestroy : function(){
25106         
25107         
25108         
25109         if(this.rendered){
25110             
25111             //for (var i =0; i < this.toolbars.length;i++) {
25112             //    // fixme - ask toolbars for heights?
25113             //    this.toolbars[i].onDestroy();
25114            // }
25115             
25116             //this.wrap.dom.innerHTML = '';
25117             //this.wrap.remove();
25118         }
25119     },
25120
25121     // private
25122     onFirstFocus : function(){
25123         
25124         this.assignDocWin();
25125         
25126         
25127         this.activated = true;
25128          
25129     
25130         if(Roo.isGecko){ // prevent silly gecko errors
25131             this.win.focus();
25132             var s = this.win.getSelection();
25133             if(!s.focusNode || s.focusNode.nodeType != 3){
25134                 var r = s.getRangeAt(0);
25135                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25136                 r.collapse(true);
25137                 this.deferFocus();
25138             }
25139             try{
25140                 this.execCmd('useCSS', true);
25141                 this.execCmd('styleWithCSS', false);
25142             }catch(e){}
25143         }
25144         this.owner.fireEvent('activate', this);
25145     },
25146
25147     // private
25148     adjustFont: function(btn){
25149         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25150         //if(Roo.isSafari){ // safari
25151         //    adjust *= 2;
25152        // }
25153         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25154         if(Roo.isSafari){ // safari
25155             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25156             v =  (v < 10) ? 10 : v;
25157             v =  (v > 48) ? 48 : v;
25158             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25159             
25160         }
25161         
25162         
25163         v = Math.max(1, v+adjust);
25164         
25165         this.execCmd('FontSize', v  );
25166     },
25167
25168     onEditorEvent : function(e)
25169     {
25170         this.owner.fireEvent('editorevent', this, e);
25171       //  this.updateToolbar();
25172         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25173     },
25174
25175     insertTag : function(tg)
25176     {
25177         // could be a bit smarter... -> wrap the current selected tRoo..
25178         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
25179             
25180             range = this.createRange(this.getSelection());
25181             var wrappingNode = this.doc.createElement(tg.toLowerCase());
25182             wrappingNode.appendChild(range.extractContents());
25183             range.insertNode(wrappingNode);
25184
25185             return;
25186             
25187             
25188             
25189         }
25190         this.execCmd("formatblock",   tg);
25191         
25192     },
25193     
25194     insertText : function(txt)
25195     {
25196         
25197         
25198         var range = this.createRange();
25199         range.deleteContents();
25200                //alert(Sender.getAttribute('label'));
25201                
25202         range.insertNode(this.doc.createTextNode(txt));
25203     } ,
25204     
25205      
25206
25207     /**
25208      * Executes a Midas editor command on the editor document and performs necessary focus and
25209      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25210      * @param {String} cmd The Midas command
25211      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25212      */
25213     relayCmd : function(cmd, value){
25214         this.win.focus();
25215         this.execCmd(cmd, value);
25216         this.owner.fireEvent('editorevent', this);
25217         //this.updateToolbar();
25218         this.owner.deferFocus();
25219     },
25220
25221     /**
25222      * Executes a Midas editor command directly on the editor document.
25223      * For visual commands, you should use {@link #relayCmd} instead.
25224      * <b>This should only be called after the editor is initialized.</b>
25225      * @param {String} cmd The Midas command
25226      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25227      */
25228     execCmd : function(cmd, value){
25229         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25230         this.syncValue();
25231     },
25232  
25233  
25234    
25235     /**
25236      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25237      * to insert tRoo.
25238      * @param {String} text | dom node.. 
25239      */
25240     insertAtCursor : function(text)
25241     {
25242         
25243         
25244         
25245         if(!this.activated){
25246             return;
25247         }
25248         /*
25249         if(Roo.isIE){
25250             this.win.focus();
25251             var r = this.doc.selection.createRange();
25252             if(r){
25253                 r.collapse(true);
25254                 r.pasteHTML(text);
25255                 this.syncValue();
25256                 this.deferFocus();
25257             
25258             }
25259             return;
25260         }
25261         */
25262         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25263             this.win.focus();
25264             
25265             
25266             // from jquery ui (MIT licenced)
25267             var range, node;
25268             var win = this.win;
25269             
25270             if (win.getSelection && win.getSelection().getRangeAt) {
25271                 range = win.getSelection().getRangeAt(0);
25272                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25273                 range.insertNode(node);
25274             } else if (win.document.selection && win.document.selection.createRange) {
25275                 // no firefox support
25276                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25277                 win.document.selection.createRange().pasteHTML(txt);
25278             } else {
25279                 // no firefox support
25280                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25281                 this.execCmd('InsertHTML', txt);
25282             } 
25283             
25284             this.syncValue();
25285             
25286             this.deferFocus();
25287         }
25288     },
25289  // private
25290     mozKeyPress : function(e){
25291         if(e.ctrlKey){
25292             var c = e.getCharCode(), cmd;
25293           
25294             if(c > 0){
25295                 c = String.fromCharCode(c).toLowerCase();
25296                 switch(c){
25297                     case 'b':
25298                         cmd = 'bold';
25299                         break;
25300                     case 'i':
25301                         cmd = 'italic';
25302                         break;
25303                     
25304                     case 'u':
25305                         cmd = 'underline';
25306                         break;
25307                     
25308                     case 'v':
25309                         this.cleanUpPaste.defer(100, this);
25310                         return;
25311                         
25312                 }
25313                 if(cmd){
25314                     this.win.focus();
25315                     this.execCmd(cmd);
25316                     this.deferFocus();
25317                     e.preventDefault();
25318                 }
25319                 
25320             }
25321         }
25322     },
25323
25324     // private
25325     fixKeys : function(){ // load time branching for fastest keydown performance
25326         if(Roo.isIE){
25327             return function(e){
25328                 var k = e.getKey(), r;
25329                 if(k == e.TAB){
25330                     e.stopEvent();
25331                     r = this.doc.selection.createRange();
25332                     if(r){
25333                         r.collapse(true);
25334                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25335                         this.deferFocus();
25336                     }
25337                     return;
25338                 }
25339                 
25340                 if(k == e.ENTER){
25341                     r = this.doc.selection.createRange();
25342                     if(r){
25343                         var target = r.parentElement();
25344                         if(!target || target.tagName.toLowerCase() != 'li'){
25345                             e.stopEvent();
25346                             r.pasteHTML('<br />');
25347                             r.collapse(false);
25348                             r.select();
25349                         }
25350                     }
25351                 }
25352                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25353                     this.cleanUpPaste.defer(100, this);
25354                     return;
25355                 }
25356                 
25357                 
25358             };
25359         }else if(Roo.isOpera){
25360             return function(e){
25361                 var k = e.getKey();
25362                 if(k == e.TAB){
25363                     e.stopEvent();
25364                     this.win.focus();
25365                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25366                     this.deferFocus();
25367                 }
25368                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25369                     this.cleanUpPaste.defer(100, this);
25370                     return;
25371                 }
25372                 
25373             };
25374         }else if(Roo.isSafari){
25375             return function(e){
25376                 var k = e.getKey();
25377                 
25378                 if(k == e.TAB){
25379                     e.stopEvent();
25380                     this.execCmd('InsertText','\t');
25381                     this.deferFocus();
25382                     return;
25383                 }
25384                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25385                     this.cleanUpPaste.defer(100, this);
25386                     return;
25387                 }
25388                 
25389              };
25390         }
25391     }(),
25392     
25393     getAllAncestors: function()
25394     {
25395         var p = this.getSelectedNode();
25396         var a = [];
25397         if (!p) {
25398             a.push(p); // push blank onto stack..
25399             p = this.getParentElement();
25400         }
25401         
25402         
25403         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25404             a.push(p);
25405             p = p.parentNode;
25406         }
25407         a.push(this.doc.body);
25408         return a;
25409     },
25410     lastSel : false,
25411     lastSelNode : false,
25412     
25413     
25414     getSelection : function() 
25415     {
25416         this.assignDocWin();
25417         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25418     },
25419     
25420     getSelectedNode: function() 
25421     {
25422         // this may only work on Gecko!!!
25423         
25424         // should we cache this!!!!
25425         
25426         
25427         
25428          
25429         var range = this.createRange(this.getSelection()).cloneRange();
25430         
25431         if (Roo.isIE) {
25432             var parent = range.parentElement();
25433             while (true) {
25434                 var testRange = range.duplicate();
25435                 testRange.moveToElementText(parent);
25436                 if (testRange.inRange(range)) {
25437                     break;
25438                 }
25439                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25440                     break;
25441                 }
25442                 parent = parent.parentElement;
25443             }
25444             return parent;
25445         }
25446         
25447         // is ancestor a text element.
25448         var ac =  range.commonAncestorContainer;
25449         if (ac.nodeType == 3) {
25450             ac = ac.parentNode;
25451         }
25452         
25453         var ar = ac.childNodes;
25454          
25455         var nodes = [];
25456         var other_nodes = [];
25457         var has_other_nodes = false;
25458         for (var i=0;i<ar.length;i++) {
25459             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25460                 continue;
25461             }
25462             // fullly contained node.
25463             
25464             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25465                 nodes.push(ar[i]);
25466                 continue;
25467             }
25468             
25469             // probably selected..
25470             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25471                 other_nodes.push(ar[i]);
25472                 continue;
25473             }
25474             // outer..
25475             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25476                 continue;
25477             }
25478             
25479             
25480             has_other_nodes = true;
25481         }
25482         if (!nodes.length && other_nodes.length) {
25483             nodes= other_nodes;
25484         }
25485         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25486             return false;
25487         }
25488         
25489         return nodes[0];
25490     },
25491     createRange: function(sel)
25492     {
25493         // this has strange effects when using with 
25494         // top toolbar - not sure if it's a great idea.
25495         //this.editor.contentWindow.focus();
25496         if (typeof sel != "undefined") {
25497             try {
25498                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25499             } catch(e) {
25500                 return this.doc.createRange();
25501             }
25502         } else {
25503             return this.doc.createRange();
25504         }
25505     },
25506     getParentElement: function()
25507     {
25508         
25509         this.assignDocWin();
25510         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25511         
25512         var range = this.createRange(sel);
25513          
25514         try {
25515             var p = range.commonAncestorContainer;
25516             while (p.nodeType == 3) { // text node
25517                 p = p.parentNode;
25518             }
25519             return p;
25520         } catch (e) {
25521             return null;
25522         }
25523     
25524     },
25525     /***
25526      *
25527      * Range intersection.. the hard stuff...
25528      *  '-1' = before
25529      *  '0' = hits..
25530      *  '1' = after.
25531      *         [ -- selected range --- ]
25532      *   [fail]                        [fail]
25533      *
25534      *    basically..
25535      *      if end is before start or  hits it. fail.
25536      *      if start is after end or hits it fail.
25537      *
25538      *   if either hits (but other is outside. - then it's not 
25539      *   
25540      *    
25541      **/
25542     
25543     
25544     // @see http://www.thismuchiknow.co.uk/?p=64.
25545     rangeIntersectsNode : function(range, node)
25546     {
25547         var nodeRange = node.ownerDocument.createRange();
25548         try {
25549             nodeRange.selectNode(node);
25550         } catch (e) {
25551             nodeRange.selectNodeContents(node);
25552         }
25553     
25554         var rangeStartRange = range.cloneRange();
25555         rangeStartRange.collapse(true);
25556     
25557         var rangeEndRange = range.cloneRange();
25558         rangeEndRange.collapse(false);
25559     
25560         var nodeStartRange = nodeRange.cloneRange();
25561         nodeStartRange.collapse(true);
25562     
25563         var nodeEndRange = nodeRange.cloneRange();
25564         nodeEndRange.collapse(false);
25565     
25566         return rangeStartRange.compareBoundaryPoints(
25567                  Range.START_TO_START, nodeEndRange) == -1 &&
25568                rangeEndRange.compareBoundaryPoints(
25569                  Range.START_TO_START, nodeStartRange) == 1;
25570         
25571          
25572     },
25573     rangeCompareNode : function(range, node)
25574     {
25575         var nodeRange = node.ownerDocument.createRange();
25576         try {
25577             nodeRange.selectNode(node);
25578         } catch (e) {
25579             nodeRange.selectNodeContents(node);
25580         }
25581         
25582         
25583         range.collapse(true);
25584     
25585         nodeRange.collapse(true);
25586      
25587         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25588         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25589          
25590         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25591         
25592         var nodeIsBefore   =  ss == 1;
25593         var nodeIsAfter    = ee == -1;
25594         
25595         if (nodeIsBefore && nodeIsAfter)
25596             return 0; // outer
25597         if (!nodeIsBefore && nodeIsAfter)
25598             return 1; //right trailed.
25599         
25600         if (nodeIsBefore && !nodeIsAfter)
25601             return 2;  // left trailed.
25602         // fully contined.
25603         return 3;
25604     },
25605
25606     // private? - in a new class?
25607     cleanUpPaste :  function()
25608     {
25609         // cleans up the whole document..
25610         Roo.log('cleanuppaste');
25611         
25612         this.cleanUpChildren(this.doc.body);
25613         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25614         if (clean != this.doc.body.innerHTML) {
25615             this.doc.body.innerHTML = clean;
25616         }
25617         
25618     },
25619     
25620     cleanWordChars : function(input) {// change the chars to hex code
25621         var he = Roo.HtmlEditorCore;
25622         
25623         var output = input;
25624         Roo.each(he.swapCodes, function(sw) { 
25625             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25626             
25627             output = output.replace(swapper, sw[1]);
25628         });
25629         
25630         return output;
25631     },
25632     
25633     
25634     cleanUpChildren : function (n)
25635     {
25636         if (!n.childNodes.length) {
25637             return;
25638         }
25639         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25640            this.cleanUpChild(n.childNodes[i]);
25641         }
25642     },
25643     
25644     
25645         
25646     
25647     cleanUpChild : function (node)
25648     {
25649         var ed = this;
25650         //console.log(node);
25651         if (node.nodeName == "#text") {
25652             // clean up silly Windows -- stuff?
25653             return; 
25654         }
25655         if (node.nodeName == "#comment") {
25656             node.parentNode.removeChild(node);
25657             // clean up silly Windows -- stuff?
25658             return; 
25659         }
25660         var lcname = node.tagName.toLowerCase();
25661         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25662         // whitelist of tags..
25663         
25664         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25665             // remove node.
25666             node.parentNode.removeChild(node);
25667             return;
25668             
25669         }
25670         
25671         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25672         
25673         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25674         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25675         
25676         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25677         //    remove_keep_children = true;
25678         //}
25679         
25680         if (remove_keep_children) {
25681             this.cleanUpChildren(node);
25682             // inserts everything just before this node...
25683             while (node.childNodes.length) {
25684                 var cn = node.childNodes[0];
25685                 node.removeChild(cn);
25686                 node.parentNode.insertBefore(cn, node);
25687             }
25688             node.parentNode.removeChild(node);
25689             return;
25690         }
25691         
25692         if (!node.attributes || !node.attributes.length) {
25693             this.cleanUpChildren(node);
25694             return;
25695         }
25696         
25697         function cleanAttr(n,v)
25698         {
25699             
25700             if (v.match(/^\./) || v.match(/^\//)) {
25701                 return;
25702             }
25703             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
25704                 return;
25705             }
25706             if (v.match(/^#/)) {
25707                 return;
25708             }
25709 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25710             node.removeAttribute(n);
25711             
25712         }
25713         
25714         var cwhite = this.cwhite;
25715         var cblack = this.cblack;
25716             
25717         function cleanStyle(n,v)
25718         {
25719             if (v.match(/expression/)) { //XSS?? should we even bother..
25720                 node.removeAttribute(n);
25721                 return;
25722             }
25723             
25724             var parts = v.split(/;/);
25725             var clean = [];
25726             
25727             Roo.each(parts, function(p) {
25728                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25729                 if (!p.length) {
25730                     return true;
25731                 }
25732                 var l = p.split(':').shift().replace(/\s+/g,'');
25733                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25734                 
25735                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25736 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25737                     //node.removeAttribute(n);
25738                     return true;
25739                 }
25740                 //Roo.log()
25741                 // only allow 'c whitelisted system attributes'
25742                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25743 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25744                     //node.removeAttribute(n);
25745                     return true;
25746                 }
25747                 
25748                 
25749                  
25750                 
25751                 clean.push(p);
25752                 return true;
25753             });
25754             if (clean.length) { 
25755                 node.setAttribute(n, clean.join(';'));
25756             } else {
25757                 node.removeAttribute(n);
25758             }
25759             
25760         }
25761         
25762         
25763         for (var i = node.attributes.length-1; i > -1 ; i--) {
25764             var a = node.attributes[i];
25765             //console.log(a);
25766             
25767             if (a.name.toLowerCase().substr(0,2)=='on')  {
25768                 node.removeAttribute(a.name);
25769                 continue;
25770             }
25771             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25772                 node.removeAttribute(a.name);
25773                 continue;
25774             }
25775             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25776                 cleanAttr(a.name,a.value); // fixme..
25777                 continue;
25778             }
25779             if (a.name == 'style') {
25780                 cleanStyle(a.name,a.value);
25781                 continue;
25782             }
25783             /// clean up MS crap..
25784             // tecnically this should be a list of valid class'es..
25785             
25786             
25787             if (a.name == 'class') {
25788                 if (a.value.match(/^Mso/)) {
25789                     node.className = '';
25790                 }
25791                 
25792                 if (a.value.match(/body/)) {
25793                     node.className = '';
25794                 }
25795                 continue;
25796             }
25797             
25798             // style cleanup!?
25799             // class cleanup?
25800             
25801         }
25802         
25803         
25804         this.cleanUpChildren(node);
25805         
25806         
25807     },
25808     
25809     /**
25810      * Clean up MS wordisms...
25811      */
25812     cleanWord : function(node)
25813     {
25814         
25815         
25816         if (!node) {
25817             this.cleanWord(this.doc.body);
25818             return;
25819         }
25820         if (node.nodeName == "#text") {
25821             // clean up silly Windows -- stuff?
25822             return; 
25823         }
25824         if (node.nodeName == "#comment") {
25825             node.parentNode.removeChild(node);
25826             // clean up silly Windows -- stuff?
25827             return; 
25828         }
25829         
25830         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25831             node.parentNode.removeChild(node);
25832             return;
25833         }
25834         
25835         // remove - but keep children..
25836         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
25837             while (node.childNodes.length) {
25838                 var cn = node.childNodes[0];
25839                 node.removeChild(cn);
25840                 node.parentNode.insertBefore(cn, node);
25841             }
25842             node.parentNode.removeChild(node);
25843             this.iterateChildren(node, this.cleanWord);
25844             return;
25845         }
25846         // clean styles
25847         if (node.className.length) {
25848             
25849             var cn = node.className.split(/\W+/);
25850             var cna = [];
25851             Roo.each(cn, function(cls) {
25852                 if (cls.match(/Mso[a-zA-Z]+/)) {
25853                     return;
25854                 }
25855                 cna.push(cls);
25856             });
25857             node.className = cna.length ? cna.join(' ') : '';
25858             if (!cna.length) {
25859                 node.removeAttribute("class");
25860             }
25861         }
25862         
25863         if (node.hasAttribute("lang")) {
25864             node.removeAttribute("lang");
25865         }
25866         
25867         if (node.hasAttribute("style")) {
25868             
25869             var styles = node.getAttribute("style").split(";");
25870             var nstyle = [];
25871             Roo.each(styles, function(s) {
25872                 if (!s.match(/:/)) {
25873                     return;
25874                 }
25875                 var kv = s.split(":");
25876                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25877                     return;
25878                 }
25879                 // what ever is left... we allow.
25880                 nstyle.push(s);
25881             });
25882             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25883             if (!nstyle.length) {
25884                 node.removeAttribute('style');
25885             }
25886         }
25887         this.iterateChildren(node, this.cleanWord);
25888         
25889         
25890         
25891     },
25892     /**
25893      * iterateChildren of a Node, calling fn each time, using this as the scole..
25894      * @param {DomNode} node node to iterate children of.
25895      * @param {Function} fn method of this class to call on each item.
25896      */
25897     iterateChildren : function(node, fn)
25898     {
25899         if (!node.childNodes.length) {
25900                 return;
25901         }
25902         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25903            fn.call(this, node.childNodes[i])
25904         }
25905     },
25906     
25907     
25908     /**
25909      * cleanTableWidths.
25910      *
25911      * Quite often pasting from word etc.. results in tables with column and widths.
25912      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25913      *
25914      */
25915     cleanTableWidths : function(node)
25916     {
25917          
25918          
25919         if (!node) {
25920             this.cleanTableWidths(this.doc.body);
25921             return;
25922         }
25923         
25924         // ignore list...
25925         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25926             return; 
25927         }
25928         Roo.log(node.tagName);
25929         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25930             this.iterateChildren(node, this.cleanTableWidths);
25931             return;
25932         }
25933         if (node.hasAttribute('width')) {
25934             node.removeAttribute('width');
25935         }
25936         
25937          
25938         if (node.hasAttribute("style")) {
25939             // pretty basic...
25940             
25941             var styles = node.getAttribute("style").split(";");
25942             var nstyle = [];
25943             Roo.each(styles, function(s) {
25944                 if (!s.match(/:/)) {
25945                     return;
25946                 }
25947                 var kv = s.split(":");
25948                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25949                     return;
25950                 }
25951                 // what ever is left... we allow.
25952                 nstyle.push(s);
25953             });
25954             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25955             if (!nstyle.length) {
25956                 node.removeAttribute('style');
25957             }
25958         }
25959         
25960         this.iterateChildren(node, this.cleanTableWidths);
25961         
25962         
25963     },
25964     
25965     
25966     
25967     
25968     domToHTML : function(currentElement, depth, nopadtext) {
25969         
25970         depth = depth || 0;
25971         nopadtext = nopadtext || false;
25972     
25973         if (!currentElement) {
25974             return this.domToHTML(this.doc.body);
25975         }
25976         
25977         //Roo.log(currentElement);
25978         var j;
25979         var allText = false;
25980         var nodeName = currentElement.nodeName;
25981         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25982         
25983         if  (nodeName == '#text') {
25984             
25985             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25986         }
25987         
25988         
25989         var ret = '';
25990         if (nodeName != 'BODY') {
25991              
25992             var i = 0;
25993             // Prints the node tagName, such as <A>, <IMG>, etc
25994             if (tagName) {
25995                 var attr = [];
25996                 for(i = 0; i < currentElement.attributes.length;i++) {
25997                     // quoting?
25998                     var aname = currentElement.attributes.item(i).name;
25999                     if (!currentElement.attributes.item(i).value.length) {
26000                         continue;
26001                     }
26002                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26003                 }
26004                 
26005                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26006             } 
26007             else {
26008                 
26009                 // eack
26010             }
26011         } else {
26012             tagName = false;
26013         }
26014         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26015             return ret;
26016         }
26017         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26018             nopadtext = true;
26019         }
26020         
26021         
26022         // Traverse the tree
26023         i = 0;
26024         var currentElementChild = currentElement.childNodes.item(i);
26025         var allText = true;
26026         var innerHTML  = '';
26027         lastnode = '';
26028         while (currentElementChild) {
26029             // Formatting code (indent the tree so it looks nice on the screen)
26030             var nopad = nopadtext;
26031             if (lastnode == 'SPAN') {
26032                 nopad  = true;
26033             }
26034             // text
26035             if  (currentElementChild.nodeName == '#text') {
26036                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26037                 toadd = nopadtext ? toadd : toadd.trim();
26038                 if (!nopad && toadd.length > 80) {
26039                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
26040                 }
26041                 innerHTML  += toadd;
26042                 
26043                 i++;
26044                 currentElementChild = currentElement.childNodes.item(i);
26045                 lastNode = '';
26046                 continue;
26047             }
26048             allText = false;
26049             
26050             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
26051                 
26052             // Recursively traverse the tree structure of the child node
26053             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
26054             lastnode = currentElementChild.nodeName;
26055             i++;
26056             currentElementChild=currentElement.childNodes.item(i);
26057         }
26058         
26059         ret += innerHTML;
26060         
26061         if (!allText) {
26062                 // The remaining code is mostly for formatting the tree
26063             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
26064         }
26065         
26066         
26067         if (tagName) {
26068             ret+= "</"+tagName+">";
26069         }
26070         return ret;
26071         
26072     },
26073         
26074     applyBlacklists : function()
26075     {
26076         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
26077         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
26078         
26079         this.white = [];
26080         this.black = [];
26081         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26082             if (b.indexOf(tag) > -1) {
26083                 return;
26084             }
26085             this.white.push(tag);
26086             
26087         }, this);
26088         
26089         Roo.each(w, function(tag) {
26090             if (b.indexOf(tag) > -1) {
26091                 return;
26092             }
26093             if (this.white.indexOf(tag) > -1) {
26094                 return;
26095             }
26096             this.white.push(tag);
26097             
26098         }, this);
26099         
26100         
26101         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26102             if (w.indexOf(tag) > -1) {
26103                 return;
26104             }
26105             this.black.push(tag);
26106             
26107         }, this);
26108         
26109         Roo.each(b, function(tag) {
26110             if (w.indexOf(tag) > -1) {
26111                 return;
26112             }
26113             if (this.black.indexOf(tag) > -1) {
26114                 return;
26115             }
26116             this.black.push(tag);
26117             
26118         }, this);
26119         
26120         
26121         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
26122         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
26123         
26124         this.cwhite = [];
26125         this.cblack = [];
26126         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26127             if (b.indexOf(tag) > -1) {
26128                 return;
26129             }
26130             this.cwhite.push(tag);
26131             
26132         }, this);
26133         
26134         Roo.each(w, function(tag) {
26135             if (b.indexOf(tag) > -1) {
26136                 return;
26137             }
26138             if (this.cwhite.indexOf(tag) > -1) {
26139                 return;
26140             }
26141             this.cwhite.push(tag);
26142             
26143         }, this);
26144         
26145         
26146         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26147             if (w.indexOf(tag) > -1) {
26148                 return;
26149             }
26150             this.cblack.push(tag);
26151             
26152         }, this);
26153         
26154         Roo.each(b, function(tag) {
26155             if (w.indexOf(tag) > -1) {
26156                 return;
26157             }
26158             if (this.cblack.indexOf(tag) > -1) {
26159                 return;
26160             }
26161             this.cblack.push(tag);
26162             
26163         }, this);
26164     },
26165     
26166     setStylesheets : function(stylesheets)
26167     {
26168         if(typeof(stylesheets) == 'string'){
26169             Roo.get(this.iframe.contentDocument.head).createChild({
26170                 tag : 'link',
26171                 rel : 'stylesheet',
26172                 type : 'text/css',
26173                 href : stylesheets
26174             });
26175             
26176             return;
26177         }
26178         var _this = this;
26179      
26180         Roo.each(stylesheets, function(s) {
26181             if(!s.length){
26182                 return;
26183             }
26184             
26185             Roo.get(_this.iframe.contentDocument.head).createChild({
26186                 tag : 'link',
26187                 rel : 'stylesheet',
26188                 type : 'text/css',
26189                 href : s
26190             });
26191         });
26192
26193         
26194     },
26195     
26196     removeStylesheets : function()
26197     {
26198         var _this = this;
26199         
26200         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26201             s.remove();
26202         });
26203     }
26204     
26205     // hide stuff that is not compatible
26206     /**
26207      * @event blur
26208      * @hide
26209      */
26210     /**
26211      * @event change
26212      * @hide
26213      */
26214     /**
26215      * @event focus
26216      * @hide
26217      */
26218     /**
26219      * @event specialkey
26220      * @hide
26221      */
26222     /**
26223      * @cfg {String} fieldClass @hide
26224      */
26225     /**
26226      * @cfg {String} focusClass @hide
26227      */
26228     /**
26229      * @cfg {String} autoCreate @hide
26230      */
26231     /**
26232      * @cfg {String} inputType @hide
26233      */
26234     /**
26235      * @cfg {String} invalidClass @hide
26236      */
26237     /**
26238      * @cfg {String} invalidText @hide
26239      */
26240     /**
26241      * @cfg {String} msgFx @hide
26242      */
26243     /**
26244      * @cfg {String} validateOnBlur @hide
26245      */
26246 });
26247
26248 Roo.HtmlEditorCore.white = [
26249         'area', 'br', 'img', 'input', 'hr', 'wbr',
26250         
26251        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26252        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26253        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26254        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26255        'table',   'ul',         'xmp', 
26256        
26257        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26258       'thead',   'tr', 
26259      
26260       'dir', 'menu', 'ol', 'ul', 'dl',
26261        
26262       'embed',  'object'
26263 ];
26264
26265
26266 Roo.HtmlEditorCore.black = [
26267     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26268         'applet', // 
26269         'base',   'basefont', 'bgsound', 'blink',  'body', 
26270         'frame',  'frameset', 'head',    'html',   'ilayer', 
26271         'iframe', 'layer',  'link',     'meta',    'object',   
26272         'script', 'style' ,'title',  'xml' // clean later..
26273 ];
26274 Roo.HtmlEditorCore.clean = [
26275     'script', 'style', 'title', 'xml'
26276 ];
26277 Roo.HtmlEditorCore.remove = [
26278     'font'
26279 ];
26280 // attributes..
26281
26282 Roo.HtmlEditorCore.ablack = [
26283     'on'
26284 ];
26285     
26286 Roo.HtmlEditorCore.aclean = [ 
26287     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26288 ];
26289
26290 // protocols..
26291 Roo.HtmlEditorCore.pwhite= [
26292         'http',  'https',  'mailto'
26293 ];
26294
26295 // white listed style attributes.
26296 Roo.HtmlEditorCore.cwhite= [
26297       //  'text-align', /// default is to allow most things..
26298       
26299          
26300 //        'font-size'//??
26301 ];
26302
26303 // black listed style attributes.
26304 Roo.HtmlEditorCore.cblack= [
26305       //  'font-size' -- this can be set by the project 
26306 ];
26307
26308
26309 Roo.HtmlEditorCore.swapCodes   =[ 
26310     [    8211, "--" ], 
26311     [    8212, "--" ], 
26312     [    8216,  "'" ],  
26313     [    8217, "'" ],  
26314     [    8220, '"' ],  
26315     [    8221, '"' ],  
26316     [    8226, "*" ],  
26317     [    8230, "..." ]
26318 ]; 
26319
26320     //<script type="text/javascript">
26321
26322 /*
26323  * Ext JS Library 1.1.1
26324  * Copyright(c) 2006-2007, Ext JS, LLC.
26325  * Licence LGPL
26326  * 
26327  */
26328  
26329  
26330 Roo.form.HtmlEditor = function(config){
26331     
26332     
26333     
26334     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
26335     
26336     if (!this.toolbars) {
26337         this.toolbars = [];
26338     }
26339     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26340     
26341     
26342 };
26343
26344 /**
26345  * @class Roo.form.HtmlEditor
26346  * @extends Roo.form.Field
26347  * Provides a lightweight HTML Editor component.
26348  *
26349  * This has been tested on Fireforx / Chrome.. IE may not be so great..
26350  * 
26351  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
26352  * supported by this editor.</b><br/><br/>
26353  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
26354  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
26355  */
26356 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
26357     /**
26358      * @cfg {Boolean} clearUp
26359      */
26360     clearUp : true,
26361       /**
26362      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26363      */
26364     toolbars : false,
26365    
26366      /**
26367      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26368      *                        Roo.resizable.
26369      */
26370     resizable : false,
26371      /**
26372      * @cfg {Number} height (in pixels)
26373      */   
26374     height: 300,
26375    /**
26376      * @cfg {Number} width (in pixels)
26377      */   
26378     width: 500,
26379     
26380     /**
26381      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26382      * 
26383      */
26384     stylesheets: false,
26385     
26386     
26387      /**
26388      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
26389      * 
26390      */
26391     cblack: false,
26392     /**
26393      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
26394      * 
26395      */
26396     cwhite: false,
26397     
26398      /**
26399      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
26400      * 
26401      */
26402     black: false,
26403     /**
26404      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
26405      * 
26406      */
26407     white: false,
26408     
26409     // id of frame..
26410     frameId: false,
26411     
26412     // private properties
26413     validationEvent : false,
26414     deferHeight: true,
26415     initialized : false,
26416     activated : false,
26417     
26418     onFocus : Roo.emptyFn,
26419     iframePad:3,
26420     hideMode:'offsets',
26421     
26422     actionMode : 'container', // defaults to hiding it...
26423     
26424     defaultAutoCreate : { // modified by initCompnoent..
26425         tag: "textarea",
26426         style:"width:500px;height:300px;",
26427         autocomplete: "new-password"
26428     },
26429
26430     // private
26431     initComponent : function(){
26432         this.addEvents({
26433             /**
26434              * @event initialize
26435              * Fires when the editor is fully initialized (including the iframe)
26436              * @param {HtmlEditor} this
26437              */
26438             initialize: true,
26439             /**
26440              * @event activate
26441              * Fires when the editor is first receives the focus. Any insertion must wait
26442              * until after this event.
26443              * @param {HtmlEditor} this
26444              */
26445             activate: true,
26446              /**
26447              * @event beforesync
26448              * Fires before the textarea is updated with content from the editor iframe. Return false
26449              * to cancel the sync.
26450              * @param {HtmlEditor} this
26451              * @param {String} html
26452              */
26453             beforesync: true,
26454              /**
26455              * @event beforepush
26456              * Fires before the iframe editor is updated with content from the textarea. Return false
26457              * to cancel the push.
26458              * @param {HtmlEditor} this
26459              * @param {String} html
26460              */
26461             beforepush: true,
26462              /**
26463              * @event sync
26464              * Fires when the textarea is updated with content from the editor iframe.
26465              * @param {HtmlEditor} this
26466              * @param {String} html
26467              */
26468             sync: true,
26469              /**
26470              * @event push
26471              * Fires when the iframe editor is updated with content from the textarea.
26472              * @param {HtmlEditor} this
26473              * @param {String} html
26474              */
26475             push: true,
26476              /**
26477              * @event editmodechange
26478              * Fires when the editor switches edit modes
26479              * @param {HtmlEditor} this
26480              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26481              */
26482             editmodechange: true,
26483             /**
26484              * @event editorevent
26485              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26486              * @param {HtmlEditor} this
26487              */
26488             editorevent: true,
26489             /**
26490              * @event firstfocus
26491              * Fires when on first focus - needed by toolbars..
26492              * @param {HtmlEditor} this
26493              */
26494             firstfocus: true,
26495             /**
26496              * @event autosave
26497              * Auto save the htmlEditor value as a file into Events
26498              * @param {HtmlEditor} this
26499              */
26500             autosave: true,
26501             /**
26502              * @event savedpreview
26503              * preview the saved version of htmlEditor
26504              * @param {HtmlEditor} this
26505              */
26506             savedpreview: true,
26507             
26508             /**
26509             * @event stylesheetsclick
26510             * Fires when press the Sytlesheets button
26511             * @param {Roo.HtmlEditorCore} this
26512             */
26513             stylesheetsclick: true
26514         });
26515         this.defaultAutoCreate =  {
26516             tag: "textarea",
26517             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
26518             autocomplete: "new-password"
26519         };
26520     },
26521
26522     /**
26523      * Protected method that will not generally be called directly. It
26524      * is called when the editor creates its toolbar. Override this method if you need to
26525      * add custom toolbar buttons.
26526      * @param {HtmlEditor} editor
26527      */
26528     createToolbar : function(editor){
26529         Roo.log("create toolbars");
26530         if (!editor.toolbars || !editor.toolbars.length) {
26531             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
26532         }
26533         
26534         for (var i =0 ; i < editor.toolbars.length;i++) {
26535             editor.toolbars[i] = Roo.factory(
26536                     typeof(editor.toolbars[i]) == 'string' ?
26537                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
26538                 Roo.form.HtmlEditor);
26539             editor.toolbars[i].init(editor);
26540         }
26541          
26542         
26543     },
26544
26545      
26546     // private
26547     onRender : function(ct, position)
26548     {
26549         var _t = this;
26550         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
26551         
26552         this.wrap = this.el.wrap({
26553             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26554         });
26555         
26556         this.editorcore.onRender(ct, position);
26557          
26558         if (this.resizable) {
26559             this.resizeEl = new Roo.Resizable(this.wrap, {
26560                 pinned : true,
26561                 wrap: true,
26562                 dynamic : true,
26563                 minHeight : this.height,
26564                 height: this.height,
26565                 handles : this.resizable,
26566                 width: this.width,
26567                 listeners : {
26568                     resize : function(r, w, h) {
26569                         _t.onResize(w,h); // -something
26570                     }
26571                 }
26572             });
26573             
26574         }
26575         this.createToolbar(this);
26576        
26577         
26578         if(!this.width){
26579             this.setSize(this.wrap.getSize());
26580         }
26581         if (this.resizeEl) {
26582             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26583             // should trigger onReize..
26584         }
26585         
26586         this.keyNav = new Roo.KeyNav(this.el, {
26587             
26588             "tab" : function(e){
26589                 e.preventDefault();
26590                 
26591                 var value = this.getValue();
26592                 
26593                 var start = this.el.dom.selectionStart;
26594                 var end = this.el.dom.selectionEnd;
26595                 
26596                 if(!e.shiftKey){
26597                     
26598                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
26599                     this.el.dom.setSelectionRange(end + 1, end + 1);
26600                     return;
26601                 }
26602                 
26603                 var f = value.substring(0, start).split("\t");
26604                 
26605                 if(f.pop().length != 0){
26606                     return;
26607                 }
26608                 
26609                 this.setValue(f.join("\t") + value.substring(end));
26610                 this.el.dom.setSelectionRange(start - 1, start - 1);
26611                 
26612             },
26613             
26614             "home" : function(e){
26615                 e.preventDefault();
26616                 
26617                 var curr = this.el.dom.selectionStart;
26618                 var lines = this.getValue().split("\n");
26619                 
26620                 if(!lines.length){
26621                     return;
26622                 }
26623                 
26624                 if(e.ctrlKey){
26625                     this.el.dom.setSelectionRange(0, 0);
26626                     return;
26627                 }
26628                 
26629                 var pos = 0;
26630                 
26631                 for (var i = 0; i < lines.length;i++) {
26632                     pos += lines[i].length;
26633                     
26634                     if(i != 0){
26635                         pos += 1;
26636                     }
26637                     
26638                     if(pos < curr){
26639                         continue;
26640                     }
26641                     
26642                     pos -= lines[i].length;
26643                     
26644                     break;
26645                 }
26646                 
26647                 if(!e.shiftKey){
26648                     this.el.dom.setSelectionRange(pos, pos);
26649                     return;
26650                 }
26651                 
26652                 this.el.dom.selectionStart = pos;
26653                 this.el.dom.selectionEnd = curr;
26654             },
26655             
26656             "end" : function(e){
26657                 e.preventDefault();
26658                 
26659                 var curr = this.el.dom.selectionStart;
26660                 var lines = this.getValue().split("\n");
26661                 
26662                 if(!lines.length){
26663                     return;
26664                 }
26665                 
26666                 if(e.ctrlKey){
26667                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
26668                     return;
26669                 }
26670                 
26671                 var pos = 0;
26672                 
26673                 for (var i = 0; i < lines.length;i++) {
26674                     
26675                     pos += lines[i].length;
26676                     
26677                     if(i != 0){
26678                         pos += 1;
26679                     }
26680                     
26681                     if(pos < curr){
26682                         continue;
26683                     }
26684                     
26685                     break;
26686                 }
26687                 
26688                 if(!e.shiftKey){
26689                     this.el.dom.setSelectionRange(pos, pos);
26690                     return;
26691                 }
26692                 
26693                 this.el.dom.selectionStart = curr;
26694                 this.el.dom.selectionEnd = pos;
26695             },
26696
26697             scope : this,
26698
26699             doRelay : function(foo, bar, hname){
26700                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
26701             },
26702
26703             forceKeyDown: true
26704         });
26705         
26706 //        if(this.autosave && this.w){
26707 //            this.autoSaveFn = setInterval(this.autosave, 1000);
26708 //        }
26709     },
26710
26711     // private
26712     onResize : function(w, h)
26713     {
26714         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
26715         var ew = false;
26716         var eh = false;
26717         
26718         if(this.el ){
26719             if(typeof w == 'number'){
26720                 var aw = w - this.wrap.getFrameWidth('lr');
26721                 this.el.setWidth(this.adjustWidth('textarea', aw));
26722                 ew = aw;
26723             }
26724             if(typeof h == 'number'){
26725                 var tbh = 0;
26726                 for (var i =0; i < this.toolbars.length;i++) {
26727                     // fixme - ask toolbars for heights?
26728                     tbh += this.toolbars[i].tb.el.getHeight();
26729                     if (this.toolbars[i].footer) {
26730                         tbh += this.toolbars[i].footer.el.getHeight();
26731                     }
26732                 }
26733                 
26734                 
26735                 
26736                 
26737                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26738                 ah -= 5; // knock a few pixes off for look..
26739 //                Roo.log(ah);
26740                 this.el.setHeight(this.adjustWidth('textarea', ah));
26741                 var eh = ah;
26742             }
26743         }
26744         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26745         this.editorcore.onResize(ew,eh);
26746         
26747     },
26748
26749     /**
26750      * Toggles the editor between standard and source edit mode.
26751      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26752      */
26753     toggleSourceEdit : function(sourceEditMode)
26754     {
26755         this.editorcore.toggleSourceEdit(sourceEditMode);
26756         
26757         if(this.editorcore.sourceEditMode){
26758             Roo.log('editor - showing textarea');
26759             
26760 //            Roo.log('in');
26761 //            Roo.log(this.syncValue());
26762             this.editorcore.syncValue();
26763             this.el.removeClass('x-hidden');
26764             this.el.dom.removeAttribute('tabIndex');
26765             this.el.focus();
26766             
26767             for (var i = 0; i < this.toolbars.length; i++) {
26768                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
26769                     this.toolbars[i].tb.hide();
26770                     this.toolbars[i].footer.hide();
26771                 }
26772             }
26773             
26774         }else{
26775             Roo.log('editor - hiding textarea');
26776 //            Roo.log('out')
26777 //            Roo.log(this.pushValue()); 
26778             this.editorcore.pushValue();
26779             
26780             this.el.addClass('x-hidden');
26781             this.el.dom.setAttribute('tabIndex', -1);
26782             
26783             for (var i = 0; i < this.toolbars.length; i++) {
26784                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
26785                     this.toolbars[i].tb.show();
26786                     this.toolbars[i].footer.show();
26787                 }
26788             }
26789             
26790             //this.deferFocus();
26791         }
26792         
26793         this.setSize(this.wrap.getSize());
26794         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
26795         
26796         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26797     },
26798  
26799     // private (for BoxComponent)
26800     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26801
26802     // private (for BoxComponent)
26803     getResizeEl : function(){
26804         return this.wrap;
26805     },
26806
26807     // private (for BoxComponent)
26808     getPositionEl : function(){
26809         return this.wrap;
26810     },
26811
26812     // private
26813     initEvents : function(){
26814         this.originalValue = this.getValue();
26815     },
26816
26817     /**
26818      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26819      * @method
26820      */
26821     markInvalid : Roo.emptyFn,
26822     /**
26823      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26824      * @method
26825      */
26826     clearInvalid : Roo.emptyFn,
26827
26828     setValue : function(v){
26829         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
26830         this.editorcore.pushValue();
26831     },
26832
26833      
26834     // private
26835     deferFocus : function(){
26836         this.focus.defer(10, this);
26837     },
26838
26839     // doc'ed in Field
26840     focus : function(){
26841         this.editorcore.focus();
26842         
26843     },
26844       
26845
26846     // private
26847     onDestroy : function(){
26848         
26849         
26850         
26851         if(this.rendered){
26852             
26853             for (var i =0; i < this.toolbars.length;i++) {
26854                 // fixme - ask toolbars for heights?
26855                 this.toolbars[i].onDestroy();
26856             }
26857             
26858             this.wrap.dom.innerHTML = '';
26859             this.wrap.remove();
26860         }
26861     },
26862
26863     // private
26864     onFirstFocus : function(){
26865         //Roo.log("onFirstFocus");
26866         this.editorcore.onFirstFocus();
26867          for (var i =0; i < this.toolbars.length;i++) {
26868             this.toolbars[i].onFirstFocus();
26869         }
26870         
26871     },
26872     
26873     // private
26874     syncValue : function()
26875     {
26876         this.editorcore.syncValue();
26877     },
26878     
26879     pushValue : function()
26880     {
26881         this.editorcore.pushValue();
26882     },
26883     
26884     setStylesheets : function(stylesheets)
26885     {
26886         this.editorcore.setStylesheets(stylesheets);
26887     },
26888     
26889     removeStylesheets : function()
26890     {
26891         this.editorcore.removeStylesheets();
26892     }
26893      
26894     
26895     // hide stuff that is not compatible
26896     /**
26897      * @event blur
26898      * @hide
26899      */
26900     /**
26901      * @event change
26902      * @hide
26903      */
26904     /**
26905      * @event focus
26906      * @hide
26907      */
26908     /**
26909      * @event specialkey
26910      * @hide
26911      */
26912     /**
26913      * @cfg {String} fieldClass @hide
26914      */
26915     /**
26916      * @cfg {String} focusClass @hide
26917      */
26918     /**
26919      * @cfg {String} autoCreate @hide
26920      */
26921     /**
26922      * @cfg {String} inputType @hide
26923      */
26924     /**
26925      * @cfg {String} invalidClass @hide
26926      */
26927     /**
26928      * @cfg {String} invalidText @hide
26929      */
26930     /**
26931      * @cfg {String} msgFx @hide
26932      */
26933     /**
26934      * @cfg {String} validateOnBlur @hide
26935      */
26936 });
26937  
26938     // <script type="text/javascript">
26939 /*
26940  * Based on
26941  * Ext JS Library 1.1.1
26942  * Copyright(c) 2006-2007, Ext JS, LLC.
26943  *  
26944  
26945  */
26946
26947 /**
26948  * @class Roo.form.HtmlEditorToolbar1
26949  * Basic Toolbar
26950  * 
26951  * Usage:
26952  *
26953  new Roo.form.HtmlEditor({
26954     ....
26955     toolbars : [
26956         new Roo.form.HtmlEditorToolbar1({
26957             disable : { fonts: 1 , format: 1, ..., ... , ...],
26958             btns : [ .... ]
26959         })
26960     }
26961      
26962  * 
26963  * @cfg {Object} disable List of elements to disable..
26964  * @cfg {Array} btns List of additional buttons.
26965  * 
26966  * 
26967  * NEEDS Extra CSS? 
26968  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26969  */
26970  
26971 Roo.form.HtmlEditor.ToolbarStandard = function(config)
26972 {
26973     
26974     Roo.apply(this, config);
26975     
26976     // default disabled, based on 'good practice'..
26977     this.disable = this.disable || {};
26978     Roo.applyIf(this.disable, {
26979         fontSize : true,
26980         colors : true,
26981         specialElements : true
26982     });
26983     
26984     
26985     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26986     // dont call parent... till later.
26987 }
26988
26989 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
26990     
26991     tb: false,
26992     
26993     rendered: false,
26994     
26995     editor : false,
26996     editorcore : false,
26997     /**
26998      * @cfg {Object} disable  List of toolbar elements to disable
26999          
27000      */
27001     disable : false,
27002     
27003     
27004      /**
27005      * @cfg {String} createLinkText The default text for the create link prompt
27006      */
27007     createLinkText : 'Please enter the URL for the link:',
27008     /**
27009      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
27010      */
27011     defaultLinkValue : 'http:/'+'/',
27012    
27013     
27014       /**
27015      * @cfg {Array} fontFamilies An array of available font families
27016      */
27017     fontFamilies : [
27018         'Arial',
27019         'Courier New',
27020         'Tahoma',
27021         'Times New Roman',
27022         'Verdana'
27023     ],
27024     
27025     specialChars : [
27026            "&#169;",
27027           "&#174;",     
27028           "&#8482;",    
27029           "&#163;" ,    
27030          // "&#8212;",    
27031           "&#8230;",    
27032           "&#247;" ,    
27033         //  "&#225;" ,     ?? a acute?
27034            "&#8364;"    , //Euro
27035        //   "&#8220;"    ,
27036         //  "&#8221;"    ,
27037         //  "&#8226;"    ,
27038           "&#176;"  //   , // degrees
27039
27040          // "&#233;"     , // e ecute
27041          // "&#250;"     , // u ecute?
27042     ],
27043     
27044     specialElements : [
27045         {
27046             text: "Insert Table",
27047             xtype: 'MenuItem',
27048             xns : Roo.Menu,
27049             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
27050                 
27051         },
27052         {    
27053             text: "Insert Image",
27054             xtype: 'MenuItem',
27055             xns : Roo.Menu,
27056             ihtml : '<img src="about:blank"/>'
27057             
27058         }
27059         
27060          
27061     ],
27062     
27063     
27064     inputElements : [ 
27065             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
27066             "input:submit", "input:button", "select", "textarea", "label" ],
27067     formats : [
27068         ["p"] ,  
27069         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
27070         ["pre"],[ "code"], 
27071         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
27072         ['div'],['span']
27073     ],
27074     
27075     cleanStyles : [
27076         "font-size"
27077     ],
27078      /**
27079      * @cfg {String} defaultFont default font to use.
27080      */
27081     defaultFont: 'tahoma',
27082    
27083     fontSelect : false,
27084     
27085     
27086     formatCombo : false,
27087     
27088     init : function(editor)
27089     {
27090         this.editor = editor;
27091         this.editorcore = editor.editorcore ? editor.editorcore : editor;
27092         var editorcore = this.editorcore;
27093         
27094         var _t = this;
27095         
27096         var fid = editorcore.frameId;
27097         var etb = this;
27098         function btn(id, toggle, handler){
27099             var xid = fid + '-'+ id ;
27100             return {
27101                 id : xid,
27102                 cmd : id,
27103                 cls : 'x-btn-icon x-edit-'+id,
27104                 enableToggle:toggle !== false,
27105                 scope: _t, // was editor...
27106                 handler:handler||_t.relayBtnCmd,
27107                 clickEvent:'mousedown',
27108                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27109                 tabIndex:-1
27110             };
27111         }
27112         
27113         
27114         
27115         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27116         this.tb = tb;
27117          // stop form submits
27118         tb.el.on('click', function(e){
27119             e.preventDefault(); // what does this do?
27120         });
27121
27122         if(!this.disable.font) { // && !Roo.isSafari){
27123             /* why no safari for fonts 
27124             editor.fontSelect = tb.el.createChild({
27125                 tag:'select',
27126                 tabIndex: -1,
27127                 cls:'x-font-select',
27128                 html: this.createFontOptions()
27129             });
27130             
27131             editor.fontSelect.on('change', function(){
27132                 var font = editor.fontSelect.dom.value;
27133                 editor.relayCmd('fontname', font);
27134                 editor.deferFocus();
27135             }, editor);
27136             
27137             tb.add(
27138                 editor.fontSelect.dom,
27139                 '-'
27140             );
27141             */
27142             
27143         };
27144         if(!this.disable.formats){
27145             this.formatCombo = new Roo.form.ComboBox({
27146                 store: new Roo.data.SimpleStore({
27147                     id : 'tag',
27148                     fields: ['tag'],
27149                     data : this.formats // from states.js
27150                 }),
27151                 blockFocus : true,
27152                 name : '',
27153                 //autoCreate : {tag: "div",  size: "20"},
27154                 displayField:'tag',
27155                 typeAhead: false,
27156                 mode: 'local',
27157                 editable : false,
27158                 triggerAction: 'all',
27159                 emptyText:'Add tag',
27160                 selectOnFocus:true,
27161                 width:135,
27162                 listeners : {
27163                     'select': function(c, r, i) {
27164                         editorcore.insertTag(r.get('tag'));
27165                         editor.focus();
27166                     }
27167                 }
27168
27169             });
27170             tb.addField(this.formatCombo);
27171             
27172         }
27173         
27174         if(!this.disable.format){
27175             tb.add(
27176                 btn('bold'),
27177                 btn('italic'),
27178                 btn('underline'),
27179                 btn('strikethrough')
27180             );
27181         };
27182         if(!this.disable.fontSize){
27183             tb.add(
27184                 '-',
27185                 
27186                 
27187                 btn('increasefontsize', false, editorcore.adjustFont),
27188                 btn('decreasefontsize', false, editorcore.adjustFont)
27189             );
27190         };
27191         
27192         
27193         if(!this.disable.colors){
27194             tb.add(
27195                 '-', {
27196                     id:editorcore.frameId +'-forecolor',
27197                     cls:'x-btn-icon x-edit-forecolor',
27198                     clickEvent:'mousedown',
27199                     tooltip: this.buttonTips['forecolor'] || undefined,
27200                     tabIndex:-1,
27201                     menu : new Roo.menu.ColorMenu({
27202                         allowReselect: true,
27203                         focus: Roo.emptyFn,
27204                         value:'000000',
27205                         plain:true,
27206                         selectHandler: function(cp, color){
27207                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
27208                             editor.deferFocus();
27209                         },
27210                         scope: editorcore,
27211                         clickEvent:'mousedown'
27212                     })
27213                 }, {
27214                     id:editorcore.frameId +'backcolor',
27215                     cls:'x-btn-icon x-edit-backcolor',
27216                     clickEvent:'mousedown',
27217                     tooltip: this.buttonTips['backcolor'] || undefined,
27218                     tabIndex:-1,
27219                     menu : new Roo.menu.ColorMenu({
27220                         focus: Roo.emptyFn,
27221                         value:'FFFFFF',
27222                         plain:true,
27223                         allowReselect: true,
27224                         selectHandler: function(cp, color){
27225                             if(Roo.isGecko){
27226                                 editorcore.execCmd('useCSS', false);
27227                                 editorcore.execCmd('hilitecolor', color);
27228                                 editorcore.execCmd('useCSS', true);
27229                                 editor.deferFocus();
27230                             }else{
27231                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
27232                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
27233                                 editor.deferFocus();
27234                             }
27235                         },
27236                         scope:editorcore,
27237                         clickEvent:'mousedown'
27238                     })
27239                 }
27240             );
27241         };
27242         // now add all the items...
27243         
27244
27245         if(!this.disable.alignments){
27246             tb.add(
27247                 '-',
27248                 btn('justifyleft'),
27249                 btn('justifycenter'),
27250                 btn('justifyright')
27251             );
27252         };
27253
27254         //if(!Roo.isSafari){
27255             if(!this.disable.links){
27256                 tb.add(
27257                     '-',
27258                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
27259                 );
27260             };
27261
27262             if(!this.disable.lists){
27263                 tb.add(
27264                     '-',
27265                     btn('insertorderedlist'),
27266                     btn('insertunorderedlist')
27267                 );
27268             }
27269             if(!this.disable.sourceEdit){
27270                 tb.add(
27271                     '-',
27272                     btn('sourceedit', true, function(btn){
27273                         this.toggleSourceEdit(btn.pressed);
27274                     })
27275                 );
27276             }
27277         //}
27278         
27279         var smenu = { };
27280         // special menu.. - needs to be tidied up..
27281         if (!this.disable.special) {
27282             smenu = {
27283                 text: "&#169;",
27284                 cls: 'x-edit-none',
27285                 
27286                 menu : {
27287                     items : []
27288                 }
27289             };
27290             for (var i =0; i < this.specialChars.length; i++) {
27291                 smenu.menu.items.push({
27292                     
27293                     html: this.specialChars[i],
27294                     handler: function(a,b) {
27295                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
27296                         //editor.insertAtCursor(a.html);
27297                         
27298                     },
27299                     tabIndex:-1
27300                 });
27301             }
27302             
27303             
27304             tb.add(smenu);
27305             
27306             
27307         }
27308         
27309         var cmenu = { };
27310         if (!this.disable.cleanStyles) {
27311             cmenu = {
27312                 cls: 'x-btn-icon x-btn-clear',
27313                 
27314                 menu : {
27315                     items : []
27316                 }
27317             };
27318             for (var i =0; i < this.cleanStyles.length; i++) {
27319                 cmenu.menu.items.push({
27320                     actiontype : this.cleanStyles[i],
27321                     html: 'Remove ' + this.cleanStyles[i],
27322                     handler: function(a,b) {
27323 //                        Roo.log(a);
27324 //                        Roo.log(b);
27325                         var c = Roo.get(editorcore.doc.body);
27326                         c.select('[style]').each(function(s) {
27327                             s.dom.style.removeProperty(a.actiontype);
27328                         });
27329                         editorcore.syncValue();
27330                     },
27331                     tabIndex:-1
27332                 });
27333             }
27334              cmenu.menu.items.push({
27335                 actiontype : 'tablewidths',
27336                 html: 'Remove Table Widths',
27337                 handler: function(a,b) {
27338                     editorcore.cleanTableWidths();
27339                     editorcore.syncValue();
27340                 },
27341                 tabIndex:-1
27342             });
27343             cmenu.menu.items.push({
27344                 actiontype : 'word',
27345                 html: 'Remove MS Word Formating',
27346                 handler: function(a,b) {
27347                     editorcore.cleanWord();
27348                     editorcore.syncValue();
27349                 },
27350                 tabIndex:-1
27351             });
27352             
27353             cmenu.menu.items.push({
27354                 actiontype : 'all',
27355                 html: 'Remove All Styles',
27356                 handler: function(a,b) {
27357                     
27358                     var c = Roo.get(editorcore.doc.body);
27359                     c.select('[style]').each(function(s) {
27360                         s.dom.removeAttribute('style');
27361                     });
27362                     editorcore.syncValue();
27363                 },
27364                 tabIndex:-1
27365             });
27366             
27367             cmenu.menu.items.push({
27368                 actiontype : 'all',
27369                 html: 'Remove All CSS Classes',
27370                 handler: function(a,b) {
27371                     
27372                     var c = Roo.get(editorcore.doc.body);
27373                     c.select('[class]').each(function(s) {
27374                         s.dom.className = '';
27375                     });
27376                     editorcore.syncValue();
27377                 },
27378                 tabIndex:-1
27379             });
27380             
27381              cmenu.menu.items.push({
27382                 actiontype : 'tidy',
27383                 html: 'Tidy HTML Source',
27384                 handler: function(a,b) {
27385                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
27386                     editorcore.syncValue();
27387                 },
27388                 tabIndex:-1
27389             });
27390             
27391             
27392             tb.add(cmenu);
27393         }
27394          
27395         if (!this.disable.specialElements) {
27396             var semenu = {
27397                 text: "Other;",
27398                 cls: 'x-edit-none',
27399                 menu : {
27400                     items : []
27401                 }
27402             };
27403             for (var i =0; i < this.specialElements.length; i++) {
27404                 semenu.menu.items.push(
27405                     Roo.apply({ 
27406                         handler: function(a,b) {
27407                             editor.insertAtCursor(this.ihtml);
27408                         }
27409                     }, this.specialElements[i])
27410                 );
27411                     
27412             }
27413             
27414             tb.add(semenu);
27415             
27416             
27417         }
27418          
27419         
27420         if (this.btns) {
27421             for(var i =0; i< this.btns.length;i++) {
27422                 var b = Roo.factory(this.btns[i],Roo.form);
27423                 b.cls =  'x-edit-none';
27424                 
27425                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
27426                     b.cls += ' x-init-enable';
27427                 }
27428                 
27429                 b.scope = editorcore;
27430                 tb.add(b);
27431             }
27432         
27433         }
27434         
27435         
27436         
27437         // disable everything...
27438         
27439         this.tb.items.each(function(item){
27440             
27441            if(
27442                 item.id != editorcore.frameId+ '-sourceedit' && 
27443                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
27444             ){
27445                 
27446                 item.disable();
27447             }
27448         });
27449         this.rendered = true;
27450         
27451         // the all the btns;
27452         editor.on('editorevent', this.updateToolbar, this);
27453         // other toolbars need to implement this..
27454         //editor.on('editmodechange', this.updateToolbar, this);
27455     },
27456     
27457     
27458     relayBtnCmd : function(btn) {
27459         this.editorcore.relayCmd(btn.cmd);
27460     },
27461     // private used internally
27462     createLink : function(){
27463         Roo.log("create link?");
27464         var url = prompt(this.createLinkText, this.defaultLinkValue);
27465         if(url && url != 'http:/'+'/'){
27466             this.editorcore.relayCmd('createlink', url);
27467         }
27468     },
27469
27470     
27471     /**
27472      * Protected method that will not generally be called directly. It triggers
27473      * a toolbar update by reading the markup state of the current selection in the editor.
27474      */
27475     updateToolbar: function(){
27476
27477         if(!this.editorcore.activated){
27478             this.editor.onFirstFocus();
27479             return;
27480         }
27481
27482         var btns = this.tb.items.map, 
27483             doc = this.editorcore.doc,
27484             frameId = this.editorcore.frameId;
27485
27486         if(!this.disable.font && !Roo.isSafari){
27487             /*
27488             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
27489             if(name != this.fontSelect.dom.value){
27490                 this.fontSelect.dom.value = name;
27491             }
27492             */
27493         }
27494         if(!this.disable.format){
27495             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
27496             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
27497             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
27498             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
27499         }
27500         if(!this.disable.alignments){
27501             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
27502             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
27503             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
27504         }
27505         if(!Roo.isSafari && !this.disable.lists){
27506             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
27507             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
27508         }
27509         
27510         var ans = this.editorcore.getAllAncestors();
27511         if (this.formatCombo) {
27512             
27513             
27514             var store = this.formatCombo.store;
27515             this.formatCombo.setValue("");
27516             for (var i =0; i < ans.length;i++) {
27517                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27518                     // select it..
27519                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27520                     break;
27521                 }
27522             }
27523         }
27524         
27525         
27526         
27527         // hides menus... - so this cant be on a menu...
27528         Roo.menu.MenuMgr.hideAll();
27529
27530         //this.editorsyncValue();
27531     },
27532    
27533     
27534     createFontOptions : function(){
27535         var buf = [], fs = this.fontFamilies, ff, lc;
27536         
27537         
27538         
27539         for(var i = 0, len = fs.length; i< len; i++){
27540             ff = fs[i];
27541             lc = ff.toLowerCase();
27542             buf.push(
27543                 '<option value="',lc,'" style="font-family:',ff,';"',
27544                     (this.defaultFont == lc ? ' selected="true">' : '>'),
27545                     ff,
27546                 '</option>'
27547             );
27548         }
27549         return buf.join('');
27550     },
27551     
27552     toggleSourceEdit : function(sourceEditMode){
27553         
27554         Roo.log("toolbar toogle");
27555         if(sourceEditMode === undefined){
27556             sourceEditMode = !this.sourceEditMode;
27557         }
27558         this.sourceEditMode = sourceEditMode === true;
27559         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
27560         // just toggle the button?
27561         if(btn.pressed !== this.sourceEditMode){
27562             btn.toggle(this.sourceEditMode);
27563             return;
27564         }
27565         
27566         if(sourceEditMode){
27567             Roo.log("disabling buttons");
27568             this.tb.items.each(function(item){
27569                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
27570                     item.disable();
27571                 }
27572             });
27573           
27574         }else{
27575             Roo.log("enabling buttons");
27576             if(this.editorcore.initialized){
27577                 this.tb.items.each(function(item){
27578                     item.enable();
27579                 });
27580             }
27581             
27582         }
27583         Roo.log("calling toggole on editor");
27584         // tell the editor that it's been pressed..
27585         this.editor.toggleSourceEdit(sourceEditMode);
27586        
27587     },
27588      /**
27589      * Object collection of toolbar tooltips for the buttons in the editor. The key
27590      * is the command id associated with that button and the value is a valid QuickTips object.
27591      * For example:
27592 <pre><code>
27593 {
27594     bold : {
27595         title: 'Bold (Ctrl+B)',
27596         text: 'Make the selected text bold.',
27597         cls: 'x-html-editor-tip'
27598     },
27599     italic : {
27600         title: 'Italic (Ctrl+I)',
27601         text: 'Make the selected text italic.',
27602         cls: 'x-html-editor-tip'
27603     },
27604     ...
27605 </code></pre>
27606     * @type Object
27607      */
27608     buttonTips : {
27609         bold : {
27610             title: 'Bold (Ctrl+B)',
27611             text: 'Make the selected text bold.',
27612             cls: 'x-html-editor-tip'
27613         },
27614         italic : {
27615             title: 'Italic (Ctrl+I)',
27616             text: 'Make the selected text italic.',
27617             cls: 'x-html-editor-tip'
27618         },
27619         underline : {
27620             title: 'Underline (Ctrl+U)',
27621             text: 'Underline the selected text.',
27622             cls: 'x-html-editor-tip'
27623         },
27624         strikethrough : {
27625             title: 'Strikethrough',
27626             text: 'Strikethrough the selected text.',
27627             cls: 'x-html-editor-tip'
27628         },
27629         increasefontsize : {
27630             title: 'Grow Text',
27631             text: 'Increase the font size.',
27632             cls: 'x-html-editor-tip'
27633         },
27634         decreasefontsize : {
27635             title: 'Shrink Text',
27636             text: 'Decrease the font size.',
27637             cls: 'x-html-editor-tip'
27638         },
27639         backcolor : {
27640             title: 'Text Highlight Color',
27641             text: 'Change the background color of the selected text.',
27642             cls: 'x-html-editor-tip'
27643         },
27644         forecolor : {
27645             title: 'Font Color',
27646             text: 'Change the color of the selected text.',
27647             cls: 'x-html-editor-tip'
27648         },
27649         justifyleft : {
27650             title: 'Align Text Left',
27651             text: 'Align text to the left.',
27652             cls: 'x-html-editor-tip'
27653         },
27654         justifycenter : {
27655             title: 'Center Text',
27656             text: 'Center text in the editor.',
27657             cls: 'x-html-editor-tip'
27658         },
27659         justifyright : {
27660             title: 'Align Text Right',
27661             text: 'Align text to the right.',
27662             cls: 'x-html-editor-tip'
27663         },
27664         insertunorderedlist : {
27665             title: 'Bullet List',
27666             text: 'Start a bulleted list.',
27667             cls: 'x-html-editor-tip'
27668         },
27669         insertorderedlist : {
27670             title: 'Numbered List',
27671             text: 'Start a numbered list.',
27672             cls: 'x-html-editor-tip'
27673         },
27674         createlink : {
27675             title: 'Hyperlink',
27676             text: 'Make the selected text a hyperlink.',
27677             cls: 'x-html-editor-tip'
27678         },
27679         sourceedit : {
27680             title: 'Source Edit',
27681             text: 'Switch to source editing mode.',
27682             cls: 'x-html-editor-tip'
27683         }
27684     },
27685     // private
27686     onDestroy : function(){
27687         if(this.rendered){
27688             
27689             this.tb.items.each(function(item){
27690                 if(item.menu){
27691                     item.menu.removeAll();
27692                     if(item.menu.el){
27693                         item.menu.el.destroy();
27694                     }
27695                 }
27696                 item.destroy();
27697             });
27698              
27699         }
27700     },
27701     onFirstFocus: function() {
27702         this.tb.items.each(function(item){
27703            item.enable();
27704         });
27705     }
27706 });
27707
27708
27709
27710
27711 // <script type="text/javascript">
27712 /*
27713  * Based on
27714  * Ext JS Library 1.1.1
27715  * Copyright(c) 2006-2007, Ext JS, LLC.
27716  *  
27717  
27718  */
27719
27720  
27721 /**
27722  * @class Roo.form.HtmlEditor.ToolbarContext
27723  * Context Toolbar
27724  * 
27725  * Usage:
27726  *
27727  new Roo.form.HtmlEditor({
27728     ....
27729     toolbars : [
27730         { xtype: 'ToolbarStandard', styles : {} }
27731         { xtype: 'ToolbarContext', disable : {} }
27732     ]
27733 })
27734
27735      
27736  * 
27737  * @config : {Object} disable List of elements to disable.. (not done yet.)
27738  * @config : {Object} styles  Map of styles available.
27739  * 
27740  */
27741
27742 Roo.form.HtmlEditor.ToolbarContext = function(config)
27743 {
27744     
27745     Roo.apply(this, config);
27746     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27747     // dont call parent... till later.
27748     this.styles = this.styles || {};
27749 }
27750
27751  
27752
27753 Roo.form.HtmlEditor.ToolbarContext.types = {
27754     'IMG' : {
27755         width : {
27756             title: "Width",
27757             width: 40
27758         },
27759         height:  {
27760             title: "Height",
27761             width: 40
27762         },
27763         align: {
27764             title: "Align",
27765             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
27766             width : 80
27767             
27768         },
27769         border: {
27770             title: "Border",
27771             width: 40
27772         },
27773         alt: {
27774             title: "Alt",
27775             width: 120
27776         },
27777         src : {
27778             title: "Src",
27779             width: 220
27780         }
27781         
27782     },
27783     'A' : {
27784         name : {
27785             title: "Name",
27786             width: 50
27787         },
27788         target:  {
27789             title: "Target",
27790             width: 120
27791         },
27792         href:  {
27793             title: "Href",
27794             width: 220
27795         } // border?
27796         
27797     },
27798     'TABLE' : {
27799         rows : {
27800             title: "Rows",
27801             width: 20
27802         },
27803         cols : {
27804             title: "Cols",
27805             width: 20
27806         },
27807         width : {
27808             title: "Width",
27809             width: 40
27810         },
27811         height : {
27812             title: "Height",
27813             width: 40
27814         },
27815         border : {
27816             title: "Border",
27817             width: 20
27818         }
27819     },
27820     'TD' : {
27821         width : {
27822             title: "Width",
27823             width: 40
27824         },
27825         height : {
27826             title: "Height",
27827             width: 40
27828         },   
27829         align: {
27830             title: "Align",
27831             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27832             width: 80
27833         },
27834         valign: {
27835             title: "Valign",
27836             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27837             width: 80
27838         },
27839         colspan: {
27840             title: "Colspan",
27841             width: 20
27842             
27843         },
27844          'font-family'  : {
27845             title : "Font",
27846             style : 'fontFamily',
27847             displayField: 'display',
27848             optname : 'font-family',
27849             width: 140
27850         }
27851     },
27852     'INPUT' : {
27853         name : {
27854             title: "name",
27855             width: 120
27856         },
27857         value : {
27858             title: "Value",
27859             width: 120
27860         },
27861         width : {
27862             title: "Width",
27863             width: 40
27864         }
27865     },
27866     'LABEL' : {
27867         'for' : {
27868             title: "For",
27869             width: 120
27870         }
27871     },
27872     'TEXTAREA' : {
27873           name : {
27874             title: "name",
27875             width: 120
27876         },
27877         rows : {
27878             title: "Rows",
27879             width: 20
27880         },
27881         cols : {
27882             title: "Cols",
27883             width: 20
27884         }
27885     },
27886     'SELECT' : {
27887         name : {
27888             title: "name",
27889             width: 120
27890         },
27891         selectoptions : {
27892             title: "Options",
27893             width: 200
27894         }
27895     },
27896     
27897     // should we really allow this??
27898     // should this just be 
27899     'BODY' : {
27900         title : {
27901             title: "Title",
27902             width: 200,
27903             disabled : true
27904         }
27905     },
27906     'SPAN' : {
27907         'font-family'  : {
27908             title : "Font",
27909             style : 'fontFamily',
27910             displayField: 'display',
27911             optname : 'font-family',
27912             width: 140
27913         }
27914     },
27915     'DIV' : {
27916         'font-family'  : {
27917             title : "Font",
27918             style : 'fontFamily',
27919             displayField: 'display',
27920             optname : 'font-family',
27921             width: 140
27922         }
27923     },
27924      'P' : {
27925         'font-family'  : {
27926             title : "Font",
27927             style : 'fontFamily',
27928             displayField: 'display',
27929             optname : 'font-family',
27930             width: 140
27931         }
27932     },
27933     
27934     '*' : {
27935         // empty..
27936     }
27937
27938 };
27939
27940 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
27941 Roo.form.HtmlEditor.ToolbarContext.stores = false;
27942
27943 Roo.form.HtmlEditor.ToolbarContext.options = {
27944         'font-family'  : [ 
27945                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
27946                 [ 'Courier New', 'Courier New'],
27947                 [ 'Tahoma', 'Tahoma'],
27948                 [ 'Times New Roman,serif', 'Times'],
27949                 [ 'Verdana','Verdana' ]
27950         ]
27951 };
27952
27953 // fixme - these need to be configurable..
27954  
27955
27956 //Roo.form.HtmlEditor.ToolbarContext.types
27957
27958
27959 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
27960     
27961     tb: false,
27962     
27963     rendered: false,
27964     
27965     editor : false,
27966     editorcore : false,
27967     /**
27968      * @cfg {Object} disable  List of toolbar elements to disable
27969          
27970      */
27971     disable : false,
27972     /**
27973      * @cfg {Object} styles List of styles 
27974      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
27975      *
27976      * These must be defined in the page, so they get rendered correctly..
27977      * .headline { }
27978      * TD.underline { }
27979      * 
27980      */
27981     styles : false,
27982     
27983     options: false,
27984     
27985     toolbars : false,
27986     
27987     init : function(editor)
27988     {
27989         this.editor = editor;
27990         this.editorcore = editor.editorcore ? editor.editorcore : editor;
27991         var editorcore = this.editorcore;
27992         
27993         var fid = editorcore.frameId;
27994         var etb = this;
27995         function btn(id, toggle, handler){
27996             var xid = fid + '-'+ id ;
27997             return {
27998                 id : xid,
27999                 cmd : id,
28000                 cls : 'x-btn-icon x-edit-'+id,
28001                 enableToggle:toggle !== false,
28002                 scope: editorcore, // was editor...
28003                 handler:handler||editorcore.relayBtnCmd,
28004                 clickEvent:'mousedown',
28005                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
28006                 tabIndex:-1
28007             };
28008         }
28009         // create a new element.
28010         var wdiv = editor.wrap.createChild({
28011                 tag: 'div'
28012             }, editor.wrap.dom.firstChild.nextSibling, true);
28013         
28014         // can we do this more than once??
28015         
28016          // stop form submits
28017       
28018  
28019         // disable everything...
28020         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
28021         this.toolbars = {};
28022            
28023         for (var i in  ty) {
28024           
28025             this.toolbars[i] = this.buildToolbar(ty[i],i);
28026         }
28027         this.tb = this.toolbars.BODY;
28028         this.tb.el.show();
28029         this.buildFooter();
28030         this.footer.show();
28031         editor.on('hide', function( ) { this.footer.hide() }, this);
28032         editor.on('show', function( ) { this.footer.show() }, this);
28033         
28034          
28035         this.rendered = true;
28036         
28037         // the all the btns;
28038         editor.on('editorevent', this.updateToolbar, this);
28039         // other toolbars need to implement this..
28040         //editor.on('editmodechange', this.updateToolbar, this);
28041     },
28042     
28043     
28044     
28045     /**
28046      * Protected method that will not generally be called directly. It triggers
28047      * a toolbar update by reading the markup state of the current selection in the editor.
28048      *
28049      * Note you can force an update by calling on('editorevent', scope, false)
28050      */
28051     updateToolbar: function(editor,ev,sel){
28052
28053         //Roo.log(ev);
28054         // capture mouse up - this is handy for selecting images..
28055         // perhaps should go somewhere else...
28056         if(!this.editorcore.activated){
28057              this.editor.onFirstFocus();
28058             return;
28059         }
28060         
28061         
28062         
28063         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
28064         // selectNode - might want to handle IE?
28065         if (ev &&
28066             (ev.type == 'mouseup' || ev.type == 'click' ) &&
28067             ev.target && ev.target.tagName == 'IMG') {
28068             // they have click on an image...
28069             // let's see if we can change the selection...
28070             sel = ev.target;
28071          
28072               var nodeRange = sel.ownerDocument.createRange();
28073             try {
28074                 nodeRange.selectNode(sel);
28075             } catch (e) {
28076                 nodeRange.selectNodeContents(sel);
28077             }
28078             //nodeRange.collapse(true);
28079             var s = this.editorcore.win.getSelection();
28080             s.removeAllRanges();
28081             s.addRange(nodeRange);
28082         }  
28083         
28084       
28085         var updateFooter = sel ? false : true;
28086         
28087         
28088         var ans = this.editorcore.getAllAncestors();
28089         
28090         // pick
28091         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
28092         
28093         if (!sel) { 
28094             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
28095             sel = sel ? sel : this.editorcore.doc.body;
28096             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
28097             
28098         }
28099         // pick a menu that exists..
28100         var tn = sel.tagName.toUpperCase();
28101         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
28102         
28103         tn = sel.tagName.toUpperCase();
28104         
28105         var lastSel = this.tb.selectedNode;
28106         
28107         this.tb.selectedNode = sel;
28108         
28109         // if current menu does not match..
28110         
28111         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
28112                 
28113             this.tb.el.hide();
28114             ///console.log("show: " + tn);
28115             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
28116             this.tb.el.show();
28117             // update name
28118             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
28119             
28120             
28121             // update attributes
28122             if (this.tb.fields) {
28123                 this.tb.fields.each(function(e) {
28124                     if (e.stylename) {
28125                         e.setValue(sel.style[e.stylename]);
28126                         return;
28127                     } 
28128                    e.setValue(sel.getAttribute(e.attrname));
28129                 });
28130             }
28131             
28132             var hasStyles = false;
28133             for(var i in this.styles) {
28134                 hasStyles = true;
28135                 break;
28136             }
28137             
28138             // update styles
28139             if (hasStyles) { 
28140                 var st = this.tb.fields.item(0);
28141                 
28142                 st.store.removeAll();
28143                
28144                 
28145                 var cn = sel.className.split(/\s+/);
28146                 
28147                 var avs = [];
28148                 if (this.styles['*']) {
28149                     
28150                     Roo.each(this.styles['*'], function(v) {
28151                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
28152                     });
28153                 }
28154                 if (this.styles[tn]) { 
28155                     Roo.each(this.styles[tn], function(v) {
28156                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
28157                     });
28158                 }
28159                 
28160                 st.store.loadData(avs);
28161                 st.collapse();
28162                 st.setValue(cn);
28163             }
28164             // flag our selected Node.
28165             this.tb.selectedNode = sel;
28166            
28167            
28168             Roo.menu.MenuMgr.hideAll();
28169
28170         }
28171         
28172         if (!updateFooter) {
28173             //this.footDisp.dom.innerHTML = ''; 
28174             return;
28175         }
28176         // update the footer
28177         //
28178         var html = '';
28179         
28180         this.footerEls = ans.reverse();
28181         Roo.each(this.footerEls, function(a,i) {
28182             if (!a) { return; }
28183             html += html.length ? ' &gt; '  :  '';
28184             
28185             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
28186             
28187         });
28188        
28189         // 
28190         var sz = this.footDisp.up('td').getSize();
28191         this.footDisp.dom.style.width = (sz.width -10) + 'px';
28192         this.footDisp.dom.style.marginLeft = '5px';
28193         
28194         this.footDisp.dom.style.overflow = 'hidden';
28195         
28196         this.footDisp.dom.innerHTML = html;
28197             
28198         //this.editorsyncValue();
28199     },
28200      
28201     
28202    
28203        
28204     // private
28205     onDestroy : function(){
28206         if(this.rendered){
28207             
28208             this.tb.items.each(function(item){
28209                 if(item.menu){
28210                     item.menu.removeAll();
28211                     if(item.menu.el){
28212                         item.menu.el.destroy();
28213                     }
28214                 }
28215                 item.destroy();
28216             });
28217              
28218         }
28219     },
28220     onFirstFocus: function() {
28221         // need to do this for all the toolbars..
28222         this.tb.items.each(function(item){
28223            item.enable();
28224         });
28225     },
28226     buildToolbar: function(tlist, nm)
28227     {
28228         var editor = this.editor;
28229         var editorcore = this.editorcore;
28230          // create a new element.
28231         var wdiv = editor.wrap.createChild({
28232                 tag: 'div'
28233             }, editor.wrap.dom.firstChild.nextSibling, true);
28234         
28235        
28236         var tb = new Roo.Toolbar(wdiv);
28237         // add the name..
28238         
28239         tb.add(nm+ ":&nbsp;");
28240         
28241         var styles = [];
28242         for(var i in this.styles) {
28243             styles.push(i);
28244         }
28245         
28246         // styles...
28247         if (styles && styles.length) {
28248             
28249             // this needs a multi-select checkbox...
28250             tb.addField( new Roo.form.ComboBox({
28251                 store: new Roo.data.SimpleStore({
28252                     id : 'val',
28253                     fields: ['val', 'selected'],
28254                     data : [] 
28255                 }),
28256                 name : '-roo-edit-className',
28257                 attrname : 'className',
28258                 displayField: 'val',
28259                 typeAhead: false,
28260                 mode: 'local',
28261                 editable : false,
28262                 triggerAction: 'all',
28263                 emptyText:'Select Style',
28264                 selectOnFocus:true,
28265                 width: 130,
28266                 listeners : {
28267                     'select': function(c, r, i) {
28268                         // initial support only for on class per el..
28269                         tb.selectedNode.className =  r ? r.get('val') : '';
28270                         editorcore.syncValue();
28271                     }
28272                 }
28273     
28274             }));
28275         }
28276         
28277         var tbc = Roo.form.HtmlEditor.ToolbarContext;
28278         var tbops = tbc.options;
28279         
28280         for (var i in tlist) {
28281             
28282             var item = tlist[i];
28283             tb.add(item.title + ":&nbsp;");
28284             
28285             
28286             //optname == used so you can configure the options available..
28287             var opts = item.opts ? item.opts : false;
28288             if (item.optname) {
28289                 opts = tbops[item.optname];
28290            
28291             }
28292             
28293             if (opts) {
28294                 // opts == pulldown..
28295                 tb.addField( new Roo.form.ComboBox({
28296                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
28297                         id : 'val',
28298                         fields: ['val', 'display'],
28299                         data : opts  
28300                     }),
28301                     name : '-roo-edit-' + i,
28302                     attrname : i,
28303                     stylename : item.style ? item.style : false,
28304                     displayField: item.displayField ? item.displayField : 'val',
28305                     valueField :  'val',
28306                     typeAhead: false,
28307                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
28308                     editable : false,
28309                     triggerAction: 'all',
28310                     emptyText:'Select',
28311                     selectOnFocus:true,
28312                     width: item.width ? item.width  : 130,
28313                     listeners : {
28314                         'select': function(c, r, i) {
28315                             if (c.stylename) {
28316                                 tb.selectedNode.style[c.stylename] =  r.get('val');
28317                                 return;
28318                             }
28319                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
28320                         }
28321                     }
28322
28323                 }));
28324                 continue;
28325                     
28326                  
28327                 
28328                 tb.addField( new Roo.form.TextField({
28329                     name: i,
28330                     width: 100,
28331                     //allowBlank:false,
28332                     value: ''
28333                 }));
28334                 continue;
28335             }
28336             tb.addField( new Roo.form.TextField({
28337                 name: '-roo-edit-' + i,
28338                 attrname : i,
28339                 
28340                 width: item.width,
28341                 //allowBlank:true,
28342                 value: '',
28343                 listeners: {
28344                     'change' : function(f, nv, ov) {
28345                         tb.selectedNode.setAttribute(f.attrname, nv);
28346                     }
28347                 }
28348             }));
28349              
28350         }
28351         
28352         var _this = this;
28353         
28354         if(nm == 'BODY'){
28355             tb.addSeparator();
28356         
28357             tb.addButton( {
28358                 text: 'Stylesheets',
28359
28360                 listeners : {
28361                     click : function ()
28362                     {
28363                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
28364                     }
28365                 }
28366             });
28367         }
28368         
28369         tb.addFill();
28370         tb.addButton( {
28371             text: 'Remove Tag',
28372     
28373             listeners : {
28374                 click : function ()
28375                 {
28376                     // remove
28377                     // undo does not work.
28378                      
28379                     var sn = tb.selectedNode;
28380                     
28381                     var pn = sn.parentNode;
28382                     
28383                     var stn =  sn.childNodes[0];
28384                     var en = sn.childNodes[sn.childNodes.length - 1 ];
28385                     while (sn.childNodes.length) {
28386                         var node = sn.childNodes[0];
28387                         sn.removeChild(node);
28388                         //Roo.log(node);
28389                         pn.insertBefore(node, sn);
28390                         
28391                     }
28392                     pn.removeChild(sn);
28393                     var range = editorcore.createRange();
28394         
28395                     range.setStart(stn,0);
28396                     range.setEnd(en,0); //????
28397                     //range.selectNode(sel);
28398                     
28399                     
28400                     var selection = editorcore.getSelection();
28401                     selection.removeAllRanges();
28402                     selection.addRange(range);
28403                     
28404                     
28405                     
28406                     //_this.updateToolbar(null, null, pn);
28407                     _this.updateToolbar(null, null, null);
28408                     _this.footDisp.dom.innerHTML = ''; 
28409                 }
28410             }
28411             
28412                     
28413                 
28414             
28415         });
28416         
28417         
28418         tb.el.on('click', function(e){
28419             e.preventDefault(); // what does this do?
28420         });
28421         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
28422         tb.el.hide();
28423         tb.name = nm;
28424         // dont need to disable them... as they will get hidden
28425         return tb;
28426          
28427         
28428     },
28429     buildFooter : function()
28430     {
28431         
28432         var fel = this.editor.wrap.createChild();
28433         this.footer = new Roo.Toolbar(fel);
28434         // toolbar has scrolly on left / right?
28435         var footDisp= new Roo.Toolbar.Fill();
28436         var _t = this;
28437         this.footer.add(
28438             {
28439                 text : '&lt;',
28440                 xtype: 'Button',
28441                 handler : function() {
28442                     _t.footDisp.scrollTo('left',0,true)
28443                 }
28444             }
28445         );
28446         this.footer.add( footDisp );
28447         this.footer.add( 
28448             {
28449                 text : '&gt;',
28450                 xtype: 'Button',
28451                 handler : function() {
28452                     // no animation..
28453                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
28454                 }
28455             }
28456         );
28457         var fel = Roo.get(footDisp.el);
28458         fel.addClass('x-editor-context');
28459         this.footDispWrap = fel; 
28460         this.footDispWrap.overflow  = 'hidden';
28461         
28462         this.footDisp = fel.createChild();
28463         this.footDispWrap.on('click', this.onContextClick, this)
28464         
28465         
28466     },
28467     onContextClick : function (ev,dom)
28468     {
28469         ev.preventDefault();
28470         var  cn = dom.className;
28471         //Roo.log(cn);
28472         if (!cn.match(/x-ed-loc-/)) {
28473             return;
28474         }
28475         var n = cn.split('-').pop();
28476         var ans = this.footerEls;
28477         var sel = ans[n];
28478         
28479          // pick
28480         var range = this.editorcore.createRange();
28481         
28482         range.selectNodeContents(sel);
28483         //range.selectNode(sel);
28484         
28485         
28486         var selection = this.editorcore.getSelection();
28487         selection.removeAllRanges();
28488         selection.addRange(range);
28489         
28490         
28491         
28492         this.updateToolbar(null, null, sel);
28493         
28494         
28495     }
28496     
28497     
28498     
28499     
28500     
28501 });
28502
28503
28504
28505
28506
28507 /*
28508  * Based on:
28509  * Ext JS Library 1.1.1
28510  * Copyright(c) 2006-2007, Ext JS, LLC.
28511  *
28512  * Originally Released Under LGPL - original licence link has changed is not relivant.
28513  *
28514  * Fork - LGPL
28515  * <script type="text/javascript">
28516  */
28517  
28518 /**
28519  * @class Roo.form.BasicForm
28520  * @extends Roo.util.Observable
28521  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
28522  * @constructor
28523  * @param {String/HTMLElement/Roo.Element} el The form element or its id
28524  * @param {Object} config Configuration options
28525  */
28526 Roo.form.BasicForm = function(el, config){
28527     this.allItems = [];
28528     this.childForms = [];
28529     Roo.apply(this, config);
28530     /*
28531      * The Roo.form.Field items in this form.
28532      * @type MixedCollection
28533      */
28534      
28535      
28536     this.items = new Roo.util.MixedCollection(false, function(o){
28537         return o.id || (o.id = Roo.id());
28538     });
28539     this.addEvents({
28540         /**
28541          * @event beforeaction
28542          * Fires before any action is performed. Return false to cancel the action.
28543          * @param {Form} this
28544          * @param {Action} action The action to be performed
28545          */
28546         beforeaction: true,
28547         /**
28548          * @event actionfailed
28549          * Fires when an action fails.
28550          * @param {Form} this
28551          * @param {Action} action The action that failed
28552          */
28553         actionfailed : true,
28554         /**
28555          * @event actioncomplete
28556          * Fires when an action is completed.
28557          * @param {Form} this
28558          * @param {Action} action The action that completed
28559          */
28560         actioncomplete : true
28561     });
28562     if(el){
28563         this.initEl(el);
28564     }
28565     Roo.form.BasicForm.superclass.constructor.call(this);
28566 };
28567
28568 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
28569     /**
28570      * @cfg {String} method
28571      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
28572      */
28573     /**
28574      * @cfg {DataReader} reader
28575      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
28576      * This is optional as there is built-in support for processing JSON.
28577      */
28578     /**
28579      * @cfg {DataReader} errorReader
28580      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
28581      * This is completely optional as there is built-in support for processing JSON.
28582      */
28583     /**
28584      * @cfg {String} url
28585      * The URL to use for form actions if one isn't supplied in the action options.
28586      */
28587     /**
28588      * @cfg {Boolean} fileUpload
28589      * Set to true if this form is a file upload.
28590      */
28591      
28592     /**
28593      * @cfg {Object} baseParams
28594      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
28595      */
28596      /**
28597      
28598     /**
28599      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
28600      */
28601     timeout: 30,
28602
28603     // private
28604     activeAction : null,
28605
28606     /**
28607      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
28608      * or setValues() data instead of when the form was first created.
28609      */
28610     trackResetOnLoad : false,
28611     
28612     
28613     /**
28614      * childForms - used for multi-tab forms
28615      * @type {Array}
28616      */
28617     childForms : false,
28618     
28619     /**
28620      * allItems - full list of fields.
28621      * @type {Array}
28622      */
28623     allItems : false,
28624     
28625     /**
28626      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
28627      * element by passing it or its id or mask the form itself by passing in true.
28628      * @type Mixed
28629      */
28630     waitMsgTarget : false,
28631
28632     // private
28633     initEl : function(el){
28634         this.el = Roo.get(el);
28635         this.id = this.el.id || Roo.id();
28636         this.el.on('submit', this.onSubmit, this);
28637         this.el.addClass('x-form');
28638     },
28639
28640     // private
28641     onSubmit : function(e){
28642         e.stopEvent();
28643     },
28644
28645     /**
28646      * Returns true if client-side validation on the form is successful.
28647      * @return Boolean
28648      */
28649     isValid : function(){
28650         var valid = true;
28651         this.items.each(function(f){
28652            if(!f.validate()){
28653                valid = false;
28654            }
28655         });
28656         return valid;
28657     },
28658
28659     /**
28660      * Returns true if any fields in this form have changed since their original load.
28661      * @return Boolean
28662      */
28663     isDirty : function(){
28664         var dirty = false;
28665         this.items.each(function(f){
28666            if(f.isDirty()){
28667                dirty = true;
28668                return false;
28669            }
28670         });
28671         return dirty;
28672     },
28673
28674     /**
28675      * Performs a predefined action (submit or load) or custom actions you define on this form.
28676      * @param {String} actionName The name of the action type
28677      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
28678      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
28679      * accept other config options):
28680      * <pre>
28681 Property          Type             Description
28682 ----------------  ---------------  ----------------------------------------------------------------------------------
28683 url               String           The url for the action (defaults to the form's url)
28684 method            String           The form method to use (defaults to the form's method, or POST if not defined)
28685 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
28686 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
28687                                    validate the form on the client (defaults to false)
28688      * </pre>
28689      * @return {BasicForm} this
28690      */
28691     doAction : function(action, options){
28692         if(typeof action == 'string'){
28693             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
28694         }
28695         if(this.fireEvent('beforeaction', this, action) !== false){
28696             this.beforeAction(action);
28697             action.run.defer(100, action);
28698         }
28699         return this;
28700     },
28701
28702     /**
28703      * Shortcut to do a submit action.
28704      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28705      * @return {BasicForm} this
28706      */
28707     submit : function(options){
28708         this.doAction('submit', options);
28709         return this;
28710     },
28711
28712     /**
28713      * Shortcut to do a load action.
28714      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28715      * @return {BasicForm} this
28716      */
28717     load : function(options){
28718         this.doAction('load', options);
28719         return this;
28720     },
28721
28722     /**
28723      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
28724      * @param {Record} record The record to edit
28725      * @return {BasicForm} this
28726      */
28727     updateRecord : function(record){
28728         record.beginEdit();
28729         var fs = record.fields;
28730         fs.each(function(f){
28731             var field = this.findField(f.name);
28732             if(field){
28733                 record.set(f.name, field.getValue());
28734             }
28735         }, this);
28736         record.endEdit();
28737         return this;
28738     },
28739
28740     /**
28741      * Loads an Roo.data.Record into this form.
28742      * @param {Record} record The record to load
28743      * @return {BasicForm} this
28744      */
28745     loadRecord : function(record){
28746         this.setValues(record.data);
28747         return this;
28748     },
28749
28750     // private
28751     beforeAction : function(action){
28752         var o = action.options;
28753         
28754        
28755         if(this.waitMsgTarget === true){
28756             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
28757         }else if(this.waitMsgTarget){
28758             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
28759             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
28760         }else {
28761             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
28762         }
28763          
28764     },
28765
28766     // private
28767     afterAction : function(action, success){
28768         this.activeAction = null;
28769         var o = action.options;
28770         
28771         if(this.waitMsgTarget === true){
28772             this.el.unmask();
28773         }else if(this.waitMsgTarget){
28774             this.waitMsgTarget.unmask();
28775         }else{
28776             Roo.MessageBox.updateProgress(1);
28777             Roo.MessageBox.hide();
28778         }
28779          
28780         if(success){
28781             if(o.reset){
28782                 this.reset();
28783             }
28784             Roo.callback(o.success, o.scope, [this, action]);
28785             this.fireEvent('actioncomplete', this, action);
28786             
28787         }else{
28788             
28789             // failure condition..
28790             // we have a scenario where updates need confirming.
28791             // eg. if a locking scenario exists..
28792             // we look for { errors : { needs_confirm : true }} in the response.
28793             if (
28794                 (typeof(action.result) != 'undefined')  &&
28795                 (typeof(action.result.errors) != 'undefined')  &&
28796                 (typeof(action.result.errors.needs_confirm) != 'undefined')
28797            ){
28798                 var _t = this;
28799                 Roo.MessageBox.confirm(
28800                     "Change requires confirmation",
28801                     action.result.errorMsg,
28802                     function(r) {
28803                         if (r != 'yes') {
28804                             return;
28805                         }
28806                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
28807                     }
28808                     
28809                 );
28810                 
28811                 
28812                 
28813                 return;
28814             }
28815             
28816             Roo.callback(o.failure, o.scope, [this, action]);
28817             // show an error message if no failed handler is set..
28818             if (!this.hasListener('actionfailed')) {
28819                 Roo.MessageBox.alert("Error",
28820                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
28821                         action.result.errorMsg :
28822                         "Saving Failed, please check your entries or try again"
28823                 );
28824             }
28825             
28826             this.fireEvent('actionfailed', this, action);
28827         }
28828         
28829     },
28830
28831     /**
28832      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
28833      * @param {String} id The value to search for
28834      * @return Field
28835      */
28836     findField : function(id){
28837         var field = this.items.get(id);
28838         if(!field){
28839             this.items.each(function(f){
28840                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
28841                     field = f;
28842                     return false;
28843                 }
28844             });
28845         }
28846         return field || null;
28847     },
28848
28849     /**
28850      * Add a secondary form to this one, 
28851      * Used to provide tabbed forms. One form is primary, with hidden values 
28852      * which mirror the elements from the other forms.
28853      * 
28854      * @param {Roo.form.Form} form to add.
28855      * 
28856      */
28857     addForm : function(form)
28858     {
28859        
28860         if (this.childForms.indexOf(form) > -1) {
28861             // already added..
28862             return;
28863         }
28864         this.childForms.push(form);
28865         var n = '';
28866         Roo.each(form.allItems, function (fe) {
28867             
28868             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
28869             if (this.findField(n)) { // already added..
28870                 return;
28871             }
28872             var add = new Roo.form.Hidden({
28873                 name : n
28874             });
28875             add.render(this.el);
28876             
28877             this.add( add );
28878         }, this);
28879         
28880     },
28881     /**
28882      * Mark fields in this form invalid in bulk.
28883      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
28884      * @return {BasicForm} this
28885      */
28886     markInvalid : function(errors){
28887         if(errors instanceof Array){
28888             for(var i = 0, len = errors.length; i < len; i++){
28889                 var fieldError = errors[i];
28890                 var f = this.findField(fieldError.id);
28891                 if(f){
28892                     f.markInvalid(fieldError.msg);
28893                 }
28894             }
28895         }else{
28896             var field, id;
28897             for(id in errors){
28898                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
28899                     field.markInvalid(errors[id]);
28900                 }
28901             }
28902         }
28903         Roo.each(this.childForms || [], function (f) {
28904             f.markInvalid(errors);
28905         });
28906         
28907         return this;
28908     },
28909
28910     /**
28911      * Set values for fields in this form in bulk.
28912      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
28913      * @return {BasicForm} this
28914      */
28915     setValues : function(values){
28916         if(values instanceof Array){ // array of objects
28917             for(var i = 0, len = values.length; i < len; i++){
28918                 var v = values[i];
28919                 var f = this.findField(v.id);
28920                 if(f){
28921                     f.setValue(v.value);
28922                     if(this.trackResetOnLoad){
28923                         f.originalValue = f.getValue();
28924                     }
28925                 }
28926             }
28927         }else{ // object hash
28928             var field, id;
28929             for(id in values){
28930                 if(typeof values[id] != 'function' && (field = this.findField(id))){
28931                     
28932                     if (field.setFromData && 
28933                         field.valueField && 
28934                         field.displayField &&
28935                         // combos' with local stores can 
28936                         // be queried via setValue()
28937                         // to set their value..
28938                         (field.store && !field.store.isLocal)
28939                         ) {
28940                         // it's a combo
28941                         var sd = { };
28942                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
28943                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
28944                         field.setFromData(sd);
28945                         
28946                     } else {
28947                         field.setValue(values[id]);
28948                     }
28949                     
28950                     
28951                     if(this.trackResetOnLoad){
28952                         field.originalValue = field.getValue();
28953                     }
28954                 }
28955             }
28956         }
28957          
28958         Roo.each(this.childForms || [], function (f) {
28959             f.setValues(values);
28960         });
28961                 
28962         return this;
28963     },
28964
28965     /**
28966      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
28967      * they are returned as an array.
28968      * @param {Boolean} asString
28969      * @return {Object}
28970      */
28971     getValues : function(asString){
28972         if (this.childForms) {
28973             // copy values from the child forms
28974             Roo.each(this.childForms, function (f) {
28975                 this.setValues(f.getValues());
28976             }, this);
28977         }
28978         
28979         
28980         
28981         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
28982         if(asString === true){
28983             return fs;
28984         }
28985         return Roo.urlDecode(fs);
28986     },
28987     
28988     /**
28989      * Returns the fields in this form as an object with key/value pairs. 
28990      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
28991      * @return {Object}
28992      */
28993     getFieldValues : function(with_hidden)
28994     {
28995         if (this.childForms) {
28996             // copy values from the child forms
28997             // should this call getFieldValues - probably not as we do not currently copy
28998             // hidden fields when we generate..
28999             Roo.each(this.childForms, function (f) {
29000                 this.setValues(f.getValues());
29001             }, this);
29002         }
29003         
29004         var ret = {};
29005         this.items.each(function(f){
29006             if (!f.getName()) {
29007                 return;
29008             }
29009             var v = f.getValue();
29010             if (f.inputType =='radio') {
29011                 if (typeof(ret[f.getName()]) == 'undefined') {
29012                     ret[f.getName()] = ''; // empty..
29013                 }
29014                 
29015                 if (!f.el.dom.checked) {
29016                     return;
29017                     
29018                 }
29019                 v = f.el.dom.value;
29020                 
29021             }
29022             
29023             // not sure if this supported any more..
29024             if ((typeof(v) == 'object') && f.getRawValue) {
29025                 v = f.getRawValue() ; // dates..
29026             }
29027             // combo boxes where name != hiddenName...
29028             if (f.name != f.getName()) {
29029                 ret[f.name] = f.getRawValue();
29030             }
29031             ret[f.getName()] = v;
29032         });
29033         
29034         return ret;
29035     },
29036
29037     /**
29038      * Clears all invalid messages in this form.
29039      * @return {BasicForm} this
29040      */
29041     clearInvalid : function(){
29042         this.items.each(function(f){
29043            f.clearInvalid();
29044         });
29045         
29046         Roo.each(this.childForms || [], function (f) {
29047             f.clearInvalid();
29048         });
29049         
29050         
29051         return this;
29052     },
29053
29054     /**
29055      * Resets this form.
29056      * @return {BasicForm} this
29057      */
29058     reset : function(){
29059         this.items.each(function(f){
29060             f.reset();
29061         });
29062         
29063         Roo.each(this.childForms || [], function (f) {
29064             f.reset();
29065         });
29066        
29067         
29068         return this;
29069     },
29070
29071     /**
29072      * Add Roo.form components to this form.
29073      * @param {Field} field1
29074      * @param {Field} field2 (optional)
29075      * @param {Field} etc (optional)
29076      * @return {BasicForm} this
29077      */
29078     add : function(){
29079         this.items.addAll(Array.prototype.slice.call(arguments, 0));
29080         return this;
29081     },
29082
29083
29084     /**
29085      * Removes a field from the items collection (does NOT remove its markup).
29086      * @param {Field} field
29087      * @return {BasicForm} this
29088      */
29089     remove : function(field){
29090         this.items.remove(field);
29091         return this;
29092     },
29093
29094     /**
29095      * Looks at the fields in this form, checks them for an id attribute,
29096      * and calls applyTo on the existing dom element with that id.
29097      * @return {BasicForm} this
29098      */
29099     render : function(){
29100         this.items.each(function(f){
29101             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
29102                 f.applyTo(f.id);
29103             }
29104         });
29105         return this;
29106     },
29107
29108     /**
29109      * Calls {@link Ext#apply} for all fields in this form with the passed object.
29110      * @param {Object} values
29111      * @return {BasicForm} this
29112      */
29113     applyToFields : function(o){
29114         this.items.each(function(f){
29115            Roo.apply(f, o);
29116         });
29117         return this;
29118     },
29119
29120     /**
29121      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
29122      * @param {Object} values
29123      * @return {BasicForm} this
29124      */
29125     applyIfToFields : function(o){
29126         this.items.each(function(f){
29127            Roo.applyIf(f, o);
29128         });
29129         return this;
29130     }
29131 });
29132
29133 // back compat
29134 Roo.BasicForm = Roo.form.BasicForm;/*
29135  * Based on:
29136  * Ext JS Library 1.1.1
29137  * Copyright(c) 2006-2007, Ext JS, LLC.
29138  *
29139  * Originally Released Under LGPL - original licence link has changed is not relivant.
29140  *
29141  * Fork - LGPL
29142  * <script type="text/javascript">
29143  */
29144
29145 /**
29146  * @class Roo.form.Form
29147  * @extends Roo.form.BasicForm
29148  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
29149  * @constructor
29150  * @param {Object} config Configuration options
29151  */
29152 Roo.form.Form = function(config){
29153     var xitems =  [];
29154     if (config.items) {
29155         xitems = config.items;
29156         delete config.items;
29157     }
29158    
29159     
29160     Roo.form.Form.superclass.constructor.call(this, null, config);
29161     this.url = this.url || this.action;
29162     if(!this.root){
29163         this.root = new Roo.form.Layout(Roo.applyIf({
29164             id: Roo.id()
29165         }, config));
29166     }
29167     this.active = this.root;
29168     /**
29169      * Array of all the buttons that have been added to this form via {@link addButton}
29170      * @type Array
29171      */
29172     this.buttons = [];
29173     this.allItems = [];
29174     this.addEvents({
29175         /**
29176          * @event clientvalidation
29177          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
29178          * @param {Form} this
29179          * @param {Boolean} valid true if the form has passed client-side validation
29180          */
29181         clientvalidation: true,
29182         /**
29183          * @event rendered
29184          * Fires when the form is rendered
29185          * @param {Roo.form.Form} form
29186          */
29187         rendered : true
29188     });
29189     
29190     if (this.progressUrl) {
29191             // push a hidden field onto the list of fields..
29192             this.addxtype( {
29193                     xns: Roo.form, 
29194                     xtype : 'Hidden', 
29195                     name : 'UPLOAD_IDENTIFIER' 
29196             });
29197         }
29198         
29199     
29200     Roo.each(xitems, this.addxtype, this);
29201     
29202     
29203     
29204 };
29205
29206 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
29207     /**
29208      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
29209      */
29210     /**
29211      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
29212      */
29213     /**
29214      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
29215      */
29216     buttonAlign:'center',
29217
29218     /**
29219      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
29220      */
29221     minButtonWidth:75,
29222
29223     /**
29224      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
29225      * This property cascades to child containers if not set.
29226      */
29227     labelAlign:'left',
29228
29229     /**
29230      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
29231      * fires a looping event with that state. This is required to bind buttons to the valid
29232      * state using the config value formBind:true on the button.
29233      */
29234     monitorValid : false,
29235
29236     /**
29237      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
29238      */
29239     monitorPoll : 200,
29240     
29241     /**
29242      * @cfg {String} progressUrl - Url to return progress data 
29243      */
29244     
29245     progressUrl : false,
29246   
29247     /**
29248      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
29249      * fields are added and the column is closed. If no fields are passed the column remains open
29250      * until end() is called.
29251      * @param {Object} config The config to pass to the column
29252      * @param {Field} field1 (optional)
29253      * @param {Field} field2 (optional)
29254      * @param {Field} etc (optional)
29255      * @return Column The column container object
29256      */
29257     column : function(c){
29258         var col = new Roo.form.Column(c);
29259         this.start(col);
29260         if(arguments.length > 1){ // duplicate code required because of Opera
29261             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29262             this.end();
29263         }
29264         return col;
29265     },
29266
29267     /**
29268      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
29269      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
29270      * until end() is called.
29271      * @param {Object} config The config to pass to the fieldset
29272      * @param {Field} field1 (optional)
29273      * @param {Field} field2 (optional)
29274      * @param {Field} etc (optional)
29275      * @return FieldSet The fieldset container object
29276      */
29277     fieldset : function(c){
29278         var fs = new Roo.form.FieldSet(c);
29279         this.start(fs);
29280         if(arguments.length > 1){ // duplicate code required because of Opera
29281             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29282             this.end();
29283         }
29284         return fs;
29285     },
29286
29287     /**
29288      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
29289      * fields are added and the container is closed. If no fields are passed the container remains open
29290      * until end() is called.
29291      * @param {Object} config The config to pass to the Layout
29292      * @param {Field} field1 (optional)
29293      * @param {Field} field2 (optional)
29294      * @param {Field} etc (optional)
29295      * @return Layout The container object
29296      */
29297     container : function(c){
29298         var l = new Roo.form.Layout(c);
29299         this.start(l);
29300         if(arguments.length > 1){ // duplicate code required because of Opera
29301             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29302             this.end();
29303         }
29304         return l;
29305     },
29306
29307     /**
29308      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
29309      * @param {Object} container A Roo.form.Layout or subclass of Layout
29310      * @return {Form} this
29311      */
29312     start : function(c){
29313         // cascade label info
29314         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
29315         this.active.stack.push(c);
29316         c.ownerCt = this.active;
29317         this.active = c;
29318         return this;
29319     },
29320
29321     /**
29322      * Closes the current open container
29323      * @return {Form} this
29324      */
29325     end : function(){
29326         if(this.active == this.root){
29327             return this;
29328         }
29329         this.active = this.active.ownerCt;
29330         return this;
29331     },
29332
29333     /**
29334      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
29335      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
29336      * as the label of the field.
29337      * @param {Field} field1
29338      * @param {Field} field2 (optional)
29339      * @param {Field} etc. (optional)
29340      * @return {Form} this
29341      */
29342     add : function(){
29343         this.active.stack.push.apply(this.active.stack, arguments);
29344         this.allItems.push.apply(this.allItems,arguments);
29345         var r = [];
29346         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
29347             if(a[i].isFormField){
29348                 r.push(a[i]);
29349             }
29350         }
29351         if(r.length > 0){
29352             Roo.form.Form.superclass.add.apply(this, r);
29353         }
29354         return this;
29355     },
29356     
29357
29358     
29359     
29360     
29361      /**
29362      * Find any element that has been added to a form, using it's ID or name
29363      * This can include framesets, columns etc. along with regular fields..
29364      * @param {String} id - id or name to find.
29365      
29366      * @return {Element} e - or false if nothing found.
29367      */
29368     findbyId : function(id)
29369     {
29370         var ret = false;
29371         if (!id) {
29372             return ret;
29373         }
29374         Roo.each(this.allItems, function(f){
29375             if (f.id == id || f.name == id ){
29376                 ret = f;
29377                 return false;
29378             }
29379         });
29380         return ret;
29381     },
29382
29383     
29384     
29385     /**
29386      * Render this form into the passed container. This should only be called once!
29387      * @param {String/HTMLElement/Element} container The element this component should be rendered into
29388      * @return {Form} this
29389      */
29390     render : function(ct)
29391     {
29392         
29393         
29394         
29395         ct = Roo.get(ct);
29396         var o = this.autoCreate || {
29397             tag: 'form',
29398             method : this.method || 'POST',
29399             id : this.id || Roo.id()
29400         };
29401         this.initEl(ct.createChild(o));
29402
29403         this.root.render(this.el);
29404         
29405        
29406              
29407         this.items.each(function(f){
29408             f.render('x-form-el-'+f.id);
29409         });
29410
29411         if(this.buttons.length > 0){
29412             // tables are required to maintain order and for correct IE layout
29413             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
29414                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
29415                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29416             }}, null, true);
29417             var tr = tb.getElementsByTagName('tr')[0];
29418             for(var i = 0, len = this.buttons.length; i < len; i++) {
29419                 var b = this.buttons[i];
29420                 var td = document.createElement('td');
29421                 td.className = 'x-form-btn-td';
29422                 b.render(tr.appendChild(td));
29423             }
29424         }
29425         if(this.monitorValid){ // initialize after render
29426             this.startMonitoring();
29427         }
29428         this.fireEvent('rendered', this);
29429         return this;
29430     },
29431
29432     /**
29433      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
29434      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29435      * object or a valid Roo.DomHelper element config
29436      * @param {Function} handler The function called when the button is clicked
29437      * @param {Object} scope (optional) The scope of the handler function
29438      * @return {Roo.Button}
29439      */
29440     addButton : function(config, handler, scope){
29441         var bc = {
29442             handler: handler,
29443             scope: scope,
29444             minWidth: this.minButtonWidth,
29445             hideParent:true
29446         };
29447         if(typeof config == "string"){
29448             bc.text = config;
29449         }else{
29450             Roo.apply(bc, config);
29451         }
29452         var btn = new Roo.Button(null, bc);
29453         this.buttons.push(btn);
29454         return btn;
29455     },
29456
29457      /**
29458      * Adds a series of form elements (using the xtype property as the factory method.
29459      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
29460      * @param {Object} config 
29461      */
29462     
29463     addxtype : function()
29464     {
29465         var ar = Array.prototype.slice.call(arguments, 0);
29466         var ret = false;
29467         for(var i = 0; i < ar.length; i++) {
29468             if (!ar[i]) {
29469                 continue; // skip -- if this happends something invalid got sent, we 
29470                 // should ignore it, as basically that interface element will not show up
29471                 // and that should be pretty obvious!!
29472             }
29473             
29474             if (Roo.form[ar[i].xtype]) {
29475                 ar[i].form = this;
29476                 var fe = Roo.factory(ar[i], Roo.form);
29477                 if (!ret) {
29478                     ret = fe;
29479                 }
29480                 fe.form = this;
29481                 if (fe.store) {
29482                     fe.store.form = this;
29483                 }
29484                 if (fe.isLayout) {  
29485                          
29486                     this.start(fe);
29487                     this.allItems.push(fe);
29488                     if (fe.items && fe.addxtype) {
29489                         fe.addxtype.apply(fe, fe.items);
29490                         delete fe.items;
29491                     }
29492                      this.end();
29493                     continue;
29494                 }
29495                 
29496                 
29497                  
29498                 this.add(fe);
29499               //  console.log('adding ' + ar[i].xtype);
29500             }
29501             if (ar[i].xtype == 'Button') {  
29502                 //console.log('adding button');
29503                 //console.log(ar[i]);
29504                 this.addButton(ar[i]);
29505                 this.allItems.push(fe);
29506                 continue;
29507             }
29508             
29509             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
29510                 alert('end is not supported on xtype any more, use items');
29511             //    this.end();
29512             //    //console.log('adding end');
29513             }
29514             
29515         }
29516         return ret;
29517     },
29518     
29519     /**
29520      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
29521      * option "monitorValid"
29522      */
29523     startMonitoring : function(){
29524         if(!this.bound){
29525             this.bound = true;
29526             Roo.TaskMgr.start({
29527                 run : this.bindHandler,
29528                 interval : this.monitorPoll || 200,
29529                 scope: this
29530             });
29531         }
29532     },
29533
29534     /**
29535      * Stops monitoring of the valid state of this form
29536      */
29537     stopMonitoring : function(){
29538         this.bound = false;
29539     },
29540
29541     // private
29542     bindHandler : function(){
29543         if(!this.bound){
29544             return false; // stops binding
29545         }
29546         var valid = true;
29547         this.items.each(function(f){
29548             if(!f.isValid(true)){
29549                 valid = false;
29550                 return false;
29551             }
29552         });
29553         for(var i = 0, len = this.buttons.length; i < len; i++){
29554             var btn = this.buttons[i];
29555             if(btn.formBind === true && btn.disabled === valid){
29556                 btn.setDisabled(!valid);
29557             }
29558         }
29559         this.fireEvent('clientvalidation', this, valid);
29560     }
29561     
29562     
29563     
29564     
29565     
29566     
29567     
29568     
29569 });
29570
29571
29572 // back compat
29573 Roo.Form = Roo.form.Form;
29574 /*
29575  * Based on:
29576  * Ext JS Library 1.1.1
29577  * Copyright(c) 2006-2007, Ext JS, LLC.
29578  *
29579  * Originally Released Under LGPL - original licence link has changed is not relivant.
29580  *
29581  * Fork - LGPL
29582  * <script type="text/javascript">
29583  */
29584
29585 // as we use this in bootstrap.
29586 Roo.namespace('Roo.form');
29587  /**
29588  * @class Roo.form.Action
29589  * Internal Class used to handle form actions
29590  * @constructor
29591  * @param {Roo.form.BasicForm} el The form element or its id
29592  * @param {Object} config Configuration options
29593  */
29594
29595  
29596  
29597 // define the action interface
29598 Roo.form.Action = function(form, options){
29599     this.form = form;
29600     this.options = options || {};
29601 };
29602 /**
29603  * Client Validation Failed
29604  * @const 
29605  */
29606 Roo.form.Action.CLIENT_INVALID = 'client';
29607 /**
29608  * Server Validation Failed
29609  * @const 
29610  */
29611 Roo.form.Action.SERVER_INVALID = 'server';
29612  /**
29613  * Connect to Server Failed
29614  * @const 
29615  */
29616 Roo.form.Action.CONNECT_FAILURE = 'connect';
29617 /**
29618  * Reading Data from Server Failed
29619  * @const 
29620  */
29621 Roo.form.Action.LOAD_FAILURE = 'load';
29622
29623 Roo.form.Action.prototype = {
29624     type : 'default',
29625     failureType : undefined,
29626     response : undefined,
29627     result : undefined,
29628
29629     // interface method
29630     run : function(options){
29631
29632     },
29633
29634     // interface method
29635     success : function(response){
29636
29637     },
29638
29639     // interface method
29640     handleResponse : function(response){
29641
29642     },
29643
29644     // default connection failure
29645     failure : function(response){
29646         
29647         this.response = response;
29648         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29649         this.form.afterAction(this, false);
29650     },
29651
29652     processResponse : function(response){
29653         this.response = response;
29654         if(!response.responseText){
29655             return true;
29656         }
29657         this.result = this.handleResponse(response);
29658         return this.result;
29659     },
29660
29661     // utility functions used internally
29662     getUrl : function(appendParams){
29663         var url = this.options.url || this.form.url || this.form.el.dom.action;
29664         if(appendParams){
29665             var p = this.getParams();
29666             if(p){
29667                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
29668             }
29669         }
29670         return url;
29671     },
29672
29673     getMethod : function(){
29674         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
29675     },
29676
29677     getParams : function(){
29678         var bp = this.form.baseParams;
29679         var p = this.options.params;
29680         if(p){
29681             if(typeof p == "object"){
29682                 p = Roo.urlEncode(Roo.applyIf(p, bp));
29683             }else if(typeof p == 'string' && bp){
29684                 p += '&' + Roo.urlEncode(bp);
29685             }
29686         }else if(bp){
29687             p = Roo.urlEncode(bp);
29688         }
29689         return p;
29690     },
29691
29692     createCallback : function(){
29693         return {
29694             success: this.success,
29695             failure: this.failure,
29696             scope: this,
29697             timeout: (this.form.timeout*1000),
29698             upload: this.form.fileUpload ? this.success : undefined
29699         };
29700     }
29701 };
29702
29703 Roo.form.Action.Submit = function(form, options){
29704     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
29705 };
29706
29707 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
29708     type : 'submit',
29709
29710     haveProgress : false,
29711     uploadComplete : false,
29712     
29713     // uploadProgress indicator.
29714     uploadProgress : function()
29715     {
29716         if (!this.form.progressUrl) {
29717             return;
29718         }
29719         
29720         if (!this.haveProgress) {
29721             Roo.MessageBox.progress("Uploading", "Uploading");
29722         }
29723         if (this.uploadComplete) {
29724            Roo.MessageBox.hide();
29725            return;
29726         }
29727         
29728         this.haveProgress = true;
29729    
29730         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
29731         
29732         var c = new Roo.data.Connection();
29733         c.request({
29734             url : this.form.progressUrl,
29735             params: {
29736                 id : uid
29737             },
29738             method: 'GET',
29739             success : function(req){
29740                //console.log(data);
29741                 var rdata = false;
29742                 var edata;
29743                 try  {
29744                    rdata = Roo.decode(req.responseText)
29745                 } catch (e) {
29746                     Roo.log("Invalid data from server..");
29747                     Roo.log(edata);
29748                     return;
29749                 }
29750                 if (!rdata || !rdata.success) {
29751                     Roo.log(rdata);
29752                     Roo.MessageBox.alert(Roo.encode(rdata));
29753                     return;
29754                 }
29755                 var data = rdata.data;
29756                 
29757                 if (this.uploadComplete) {
29758                    Roo.MessageBox.hide();
29759                    return;
29760                 }
29761                    
29762                 if (data){
29763                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
29764                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
29765                     );
29766                 }
29767                 this.uploadProgress.defer(2000,this);
29768             },
29769        
29770             failure: function(data) {
29771                 Roo.log('progress url failed ');
29772                 Roo.log(data);
29773             },
29774             scope : this
29775         });
29776            
29777     },
29778     
29779     
29780     run : function()
29781     {
29782         // run get Values on the form, so it syncs any secondary forms.
29783         this.form.getValues();
29784         
29785         var o = this.options;
29786         var method = this.getMethod();
29787         var isPost = method == 'POST';
29788         if(o.clientValidation === false || this.form.isValid()){
29789             
29790             if (this.form.progressUrl) {
29791                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
29792                     (new Date() * 1) + '' + Math.random());
29793                     
29794             } 
29795             
29796             
29797             Roo.Ajax.request(Roo.apply(this.createCallback(), {
29798                 form:this.form.el.dom,
29799                 url:this.getUrl(!isPost),
29800                 method: method,
29801                 params:isPost ? this.getParams() : null,
29802                 isUpload: this.form.fileUpload
29803             }));
29804             
29805             this.uploadProgress();
29806
29807         }else if (o.clientValidation !== false){ // client validation failed
29808             this.failureType = Roo.form.Action.CLIENT_INVALID;
29809             this.form.afterAction(this, false);
29810         }
29811     },
29812
29813     success : function(response)
29814     {
29815         this.uploadComplete= true;
29816         if (this.haveProgress) {
29817             Roo.MessageBox.hide();
29818         }
29819         
29820         
29821         var result = this.processResponse(response);
29822         if(result === true || result.success){
29823             this.form.afterAction(this, true);
29824             return;
29825         }
29826         if(result.errors){
29827             this.form.markInvalid(result.errors);
29828             this.failureType = Roo.form.Action.SERVER_INVALID;
29829         }
29830         this.form.afterAction(this, false);
29831     },
29832     failure : function(response)
29833     {
29834         this.uploadComplete= true;
29835         if (this.haveProgress) {
29836             Roo.MessageBox.hide();
29837         }
29838         
29839         this.response = response;
29840         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29841         this.form.afterAction(this, false);
29842     },
29843     
29844     handleResponse : function(response){
29845         if(this.form.errorReader){
29846             var rs = this.form.errorReader.read(response);
29847             var errors = [];
29848             if(rs.records){
29849                 for(var i = 0, len = rs.records.length; i < len; i++) {
29850                     var r = rs.records[i];
29851                     errors[i] = r.data;
29852                 }
29853             }
29854             if(errors.length < 1){
29855                 errors = null;
29856             }
29857             return {
29858                 success : rs.success,
29859                 errors : errors
29860             };
29861         }
29862         var ret = false;
29863         try {
29864             ret = Roo.decode(response.responseText);
29865         } catch (e) {
29866             ret = {
29867                 success: false,
29868                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
29869                 errors : []
29870             };
29871         }
29872         return ret;
29873         
29874     }
29875 });
29876
29877
29878 Roo.form.Action.Load = function(form, options){
29879     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
29880     this.reader = this.form.reader;
29881 };
29882
29883 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
29884     type : 'load',
29885
29886     run : function(){
29887         
29888         Roo.Ajax.request(Roo.apply(
29889                 this.createCallback(), {
29890                     method:this.getMethod(),
29891                     url:this.getUrl(false),
29892                     params:this.getParams()
29893         }));
29894     },
29895
29896     success : function(response){
29897         
29898         var result = this.processResponse(response);
29899         if(result === true || !result.success || !result.data){
29900             this.failureType = Roo.form.Action.LOAD_FAILURE;
29901             this.form.afterAction(this, false);
29902             return;
29903         }
29904         this.form.clearInvalid();
29905         this.form.setValues(result.data);
29906         this.form.afterAction(this, true);
29907     },
29908
29909     handleResponse : function(response){
29910         if(this.form.reader){
29911             var rs = this.form.reader.read(response);
29912             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
29913             return {
29914                 success : rs.success,
29915                 data : data
29916             };
29917         }
29918         return Roo.decode(response.responseText);
29919     }
29920 });
29921
29922 Roo.form.Action.ACTION_TYPES = {
29923     'load' : Roo.form.Action.Load,
29924     'submit' : Roo.form.Action.Submit
29925 };/*
29926  * Based on:
29927  * Ext JS Library 1.1.1
29928  * Copyright(c) 2006-2007, Ext JS, LLC.
29929  *
29930  * Originally Released Under LGPL - original licence link has changed is not relivant.
29931  *
29932  * Fork - LGPL
29933  * <script type="text/javascript">
29934  */
29935  
29936 /**
29937  * @class Roo.form.Layout
29938  * @extends Roo.Component
29939  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
29940  * @constructor
29941  * @param {Object} config Configuration options
29942  */
29943 Roo.form.Layout = function(config){
29944     var xitems = [];
29945     if (config.items) {
29946         xitems = config.items;
29947         delete config.items;
29948     }
29949     Roo.form.Layout.superclass.constructor.call(this, config);
29950     this.stack = [];
29951     Roo.each(xitems, this.addxtype, this);
29952      
29953 };
29954
29955 Roo.extend(Roo.form.Layout, Roo.Component, {
29956     /**
29957      * @cfg {String/Object} autoCreate
29958      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
29959      */
29960     /**
29961      * @cfg {String/Object/Function} style
29962      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
29963      * a function which returns such a specification.
29964      */
29965     /**
29966      * @cfg {String} labelAlign
29967      * Valid values are "left," "top" and "right" (defaults to "left")
29968      */
29969     /**
29970      * @cfg {Number} labelWidth
29971      * Fixed width in pixels of all field labels (defaults to undefined)
29972      */
29973     /**
29974      * @cfg {Boolean} clear
29975      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
29976      */
29977     clear : true,
29978     /**
29979      * @cfg {String} labelSeparator
29980      * The separator to use after field labels (defaults to ':')
29981      */
29982     labelSeparator : ':',
29983     /**
29984      * @cfg {Boolean} hideLabels
29985      * True to suppress the display of field labels in this layout (defaults to false)
29986      */
29987     hideLabels : false,
29988
29989     // private
29990     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
29991     
29992     isLayout : true,
29993     
29994     // private
29995     onRender : function(ct, position){
29996         if(this.el){ // from markup
29997             this.el = Roo.get(this.el);
29998         }else {  // generate
29999             var cfg = this.getAutoCreate();
30000             this.el = ct.createChild(cfg, position);
30001         }
30002         if(this.style){
30003             this.el.applyStyles(this.style);
30004         }
30005         if(this.labelAlign){
30006             this.el.addClass('x-form-label-'+this.labelAlign);
30007         }
30008         if(this.hideLabels){
30009             this.labelStyle = "display:none";
30010             this.elementStyle = "padding-left:0;";
30011         }else{
30012             if(typeof this.labelWidth == 'number'){
30013                 this.labelStyle = "width:"+this.labelWidth+"px;";
30014                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
30015             }
30016             if(this.labelAlign == 'top'){
30017                 this.labelStyle = "width:auto;";
30018                 this.elementStyle = "padding-left:0;";
30019             }
30020         }
30021         var stack = this.stack;
30022         var slen = stack.length;
30023         if(slen > 0){
30024             if(!this.fieldTpl){
30025                 var t = new Roo.Template(
30026                     '<div class="x-form-item {5}">',
30027                         '<label for="{0}" style="{2}">{1}{4}</label>',
30028                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30029                         '</div>',
30030                     '</div><div class="x-form-clear-left"></div>'
30031                 );
30032                 t.disableFormats = true;
30033                 t.compile();
30034                 Roo.form.Layout.prototype.fieldTpl = t;
30035             }
30036             for(var i = 0; i < slen; i++) {
30037                 if(stack[i].isFormField){
30038                     this.renderField(stack[i]);
30039                 }else{
30040                     this.renderComponent(stack[i]);
30041                 }
30042             }
30043         }
30044         if(this.clear){
30045             this.el.createChild({cls:'x-form-clear'});
30046         }
30047     },
30048
30049     // private
30050     renderField : function(f){
30051         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
30052                f.id, //0
30053                f.fieldLabel, //1
30054                f.labelStyle||this.labelStyle||'', //2
30055                this.elementStyle||'', //3
30056                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
30057                f.itemCls||this.itemCls||''  //5
30058        ], true).getPrevSibling());
30059     },
30060
30061     // private
30062     renderComponent : function(c){
30063         c.render(c.isLayout ? this.el : this.el.createChild());    
30064     },
30065     /**
30066      * Adds a object form elements (using the xtype property as the factory method.)
30067      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
30068      * @param {Object} config 
30069      */
30070     addxtype : function(o)
30071     {
30072         // create the lement.
30073         o.form = this.form;
30074         var fe = Roo.factory(o, Roo.form);
30075         this.form.allItems.push(fe);
30076         this.stack.push(fe);
30077         
30078         if (fe.isFormField) {
30079             this.form.items.add(fe);
30080         }
30081          
30082         return fe;
30083     }
30084 });
30085
30086 /**
30087  * @class Roo.form.Column
30088  * @extends Roo.form.Layout
30089  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
30090  * @constructor
30091  * @param {Object} config Configuration options
30092  */
30093 Roo.form.Column = function(config){
30094     Roo.form.Column.superclass.constructor.call(this, config);
30095 };
30096
30097 Roo.extend(Roo.form.Column, Roo.form.Layout, {
30098     /**
30099      * @cfg {Number/String} width
30100      * The fixed width of the column in pixels or CSS value (defaults to "auto")
30101      */
30102     /**
30103      * @cfg {String/Object} autoCreate
30104      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
30105      */
30106
30107     // private
30108     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
30109
30110     // private
30111     onRender : function(ct, position){
30112         Roo.form.Column.superclass.onRender.call(this, ct, position);
30113         if(this.width){
30114             this.el.setWidth(this.width);
30115         }
30116     }
30117 });
30118
30119
30120 /**
30121  * @class Roo.form.Row
30122  * @extends Roo.form.Layout
30123  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
30124  * @constructor
30125  * @param {Object} config Configuration options
30126  */
30127
30128  
30129 Roo.form.Row = function(config){
30130     Roo.form.Row.superclass.constructor.call(this, config);
30131 };
30132  
30133 Roo.extend(Roo.form.Row, Roo.form.Layout, {
30134       /**
30135      * @cfg {Number/String} width
30136      * The fixed width of the column in pixels or CSS value (defaults to "auto")
30137      */
30138     /**
30139      * @cfg {Number/String} height
30140      * The fixed height of the column in pixels or CSS value (defaults to "auto")
30141      */
30142     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
30143     
30144     padWidth : 20,
30145     // private
30146     onRender : function(ct, position){
30147         //console.log('row render');
30148         if(!this.rowTpl){
30149             var t = new Roo.Template(
30150                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
30151                     '<label for="{0}" style="{2}">{1}{4}</label>',
30152                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30153                     '</div>',
30154                 '</div>'
30155             );
30156             t.disableFormats = true;
30157             t.compile();
30158             Roo.form.Layout.prototype.rowTpl = t;
30159         }
30160         this.fieldTpl = this.rowTpl;
30161         
30162         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
30163         var labelWidth = 100;
30164         
30165         if ((this.labelAlign != 'top')) {
30166             if (typeof this.labelWidth == 'number') {
30167                 labelWidth = this.labelWidth
30168             }
30169             this.padWidth =  20 + labelWidth;
30170             
30171         }
30172         
30173         Roo.form.Column.superclass.onRender.call(this, ct, position);
30174         if(this.width){
30175             this.el.setWidth(this.width);
30176         }
30177         if(this.height){
30178             this.el.setHeight(this.height);
30179         }
30180     },
30181     
30182     // private
30183     renderField : function(f){
30184         f.fieldEl = this.fieldTpl.append(this.el, [
30185                f.id, f.fieldLabel,
30186                f.labelStyle||this.labelStyle||'',
30187                this.elementStyle||'',
30188                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
30189                f.itemCls||this.itemCls||'',
30190                f.width ? f.width + this.padWidth : 160 + this.padWidth
30191        ],true);
30192     }
30193 });
30194  
30195
30196 /**
30197  * @class Roo.form.FieldSet
30198  * @extends Roo.form.Layout
30199  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
30200  * @constructor
30201  * @param {Object} config Configuration options
30202  */
30203 Roo.form.FieldSet = function(config){
30204     Roo.form.FieldSet.superclass.constructor.call(this, config);
30205 };
30206
30207 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
30208     /**
30209      * @cfg {String} legend
30210      * The text to display as the legend for the FieldSet (defaults to '')
30211      */
30212     /**
30213      * @cfg {String/Object} autoCreate
30214      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
30215      */
30216
30217     // private
30218     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
30219
30220     // private
30221     onRender : function(ct, position){
30222         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
30223         if(this.legend){
30224             this.setLegend(this.legend);
30225         }
30226     },
30227
30228     // private
30229     setLegend : function(text){
30230         if(this.rendered){
30231             this.el.child('legend').update(text);
30232         }
30233     }
30234 });/*
30235  * Based on:
30236  * Ext JS Library 1.1.1
30237  * Copyright(c) 2006-2007, Ext JS, LLC.
30238  *
30239  * Originally Released Under LGPL - original licence link has changed is not relivant.
30240  *
30241  * Fork - LGPL
30242  * <script type="text/javascript">
30243  */
30244 /**
30245  * @class Roo.form.VTypes
30246  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
30247  * @singleton
30248  */
30249 Roo.form.VTypes = function(){
30250     // closure these in so they are only created once.
30251     var alpha = /^[a-zA-Z_]+$/;
30252     var alphanum = /^[a-zA-Z0-9_]+$/;
30253     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
30254     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
30255
30256     // All these messages and functions are configurable
30257     return {
30258         /**
30259          * The function used to validate email addresses
30260          * @param {String} value The email address
30261          */
30262         'email' : function(v){
30263             return email.test(v);
30264         },
30265         /**
30266          * The error text to display when the email validation function returns false
30267          * @type String
30268          */
30269         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
30270         /**
30271          * The keystroke filter mask to be applied on email input
30272          * @type RegExp
30273          */
30274         'emailMask' : /[a-z0-9_\.\-@]/i,
30275
30276         /**
30277          * The function used to validate URLs
30278          * @param {String} value The URL
30279          */
30280         'url' : function(v){
30281             return url.test(v);
30282         },
30283         /**
30284          * The error text to display when the url validation function returns false
30285          * @type String
30286          */
30287         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
30288         
30289         /**
30290          * The function used to validate alpha values
30291          * @param {String} value The value
30292          */
30293         'alpha' : function(v){
30294             return alpha.test(v);
30295         },
30296         /**
30297          * The error text to display when the alpha validation function returns false
30298          * @type String
30299          */
30300         'alphaText' : 'This field should only contain letters and _',
30301         /**
30302          * The keystroke filter mask to be applied on alpha input
30303          * @type RegExp
30304          */
30305         'alphaMask' : /[a-z_]/i,
30306
30307         /**
30308          * The function used to validate alphanumeric values
30309          * @param {String} value The value
30310          */
30311         'alphanum' : function(v){
30312             return alphanum.test(v);
30313         },
30314         /**
30315          * The error text to display when the alphanumeric validation function returns false
30316          * @type String
30317          */
30318         'alphanumText' : 'This field should only contain letters, numbers and _',
30319         /**
30320          * The keystroke filter mask to be applied on alphanumeric input
30321          * @type RegExp
30322          */
30323         'alphanumMask' : /[a-z0-9_]/i
30324     };
30325 }();//<script type="text/javascript">
30326
30327 /**
30328  * @class Roo.form.FCKeditor
30329  * @extends Roo.form.TextArea
30330  * Wrapper around the FCKEditor http://www.fckeditor.net
30331  * @constructor
30332  * Creates a new FCKeditor
30333  * @param {Object} config Configuration options
30334  */
30335 Roo.form.FCKeditor = function(config){
30336     Roo.form.FCKeditor.superclass.constructor.call(this, config);
30337     this.addEvents({
30338          /**
30339          * @event editorinit
30340          * Fired when the editor is initialized - you can add extra handlers here..
30341          * @param {FCKeditor} this
30342          * @param {Object} the FCK object.
30343          */
30344         editorinit : true
30345     });
30346     
30347     
30348 };
30349 Roo.form.FCKeditor.editors = { };
30350 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
30351 {
30352     //defaultAutoCreate : {
30353     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
30354     //},
30355     // private
30356     /**
30357      * @cfg {Object} fck options - see fck manual for details.
30358      */
30359     fckconfig : false,
30360     
30361     /**
30362      * @cfg {Object} fck toolbar set (Basic or Default)
30363      */
30364     toolbarSet : 'Basic',
30365     /**
30366      * @cfg {Object} fck BasePath
30367      */ 
30368     basePath : '/fckeditor/',
30369     
30370     
30371     frame : false,
30372     
30373     value : '',
30374     
30375    
30376     onRender : function(ct, position)
30377     {
30378         if(!this.el){
30379             this.defaultAutoCreate = {
30380                 tag: "textarea",
30381                 style:"width:300px;height:60px;",
30382                 autocomplete: "new-password"
30383             };
30384         }
30385         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
30386         /*
30387         if(this.grow){
30388             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
30389             if(this.preventScrollbars){
30390                 this.el.setStyle("overflow", "hidden");
30391             }
30392             this.el.setHeight(this.growMin);
30393         }
30394         */
30395         //console.log('onrender' + this.getId() );
30396         Roo.form.FCKeditor.editors[this.getId()] = this;
30397          
30398
30399         this.replaceTextarea() ;
30400         
30401     },
30402     
30403     getEditor : function() {
30404         return this.fckEditor;
30405     },
30406     /**
30407      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
30408      * @param {Mixed} value The value to set
30409      */
30410     
30411     
30412     setValue : function(value)
30413     {
30414         //console.log('setValue: ' + value);
30415         
30416         if(typeof(value) == 'undefined') { // not sure why this is happending...
30417             return;
30418         }
30419         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
30420         
30421         //if(!this.el || !this.getEditor()) {
30422         //    this.value = value;
30423             //this.setValue.defer(100,this,[value]);    
30424         //    return;
30425         //} 
30426         
30427         if(!this.getEditor()) {
30428             return;
30429         }
30430         
30431         this.getEditor().SetData(value);
30432         
30433         //
30434
30435     },
30436
30437     /**
30438      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
30439      * @return {Mixed} value The field value
30440      */
30441     getValue : function()
30442     {
30443         
30444         if (this.frame && this.frame.dom.style.display == 'none') {
30445             return Roo.form.FCKeditor.superclass.getValue.call(this);
30446         }
30447         
30448         if(!this.el || !this.getEditor()) {
30449            
30450            // this.getValue.defer(100,this); 
30451             return this.value;
30452         }
30453        
30454         
30455         var value=this.getEditor().GetData();
30456         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
30457         return Roo.form.FCKeditor.superclass.getValue.call(this);
30458         
30459
30460     },
30461
30462     /**
30463      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
30464      * @return {Mixed} value The field value
30465      */
30466     getRawValue : function()
30467     {
30468         if (this.frame && this.frame.dom.style.display == 'none') {
30469             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30470         }
30471         
30472         if(!this.el || !this.getEditor()) {
30473             //this.getRawValue.defer(100,this); 
30474             return this.value;
30475             return;
30476         }
30477         
30478         
30479         
30480         var value=this.getEditor().GetData();
30481         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
30482         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30483          
30484     },
30485     
30486     setSize : function(w,h) {
30487         
30488         
30489         
30490         //if (this.frame && this.frame.dom.style.display == 'none') {
30491         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30492         //    return;
30493         //}
30494         //if(!this.el || !this.getEditor()) {
30495         //    this.setSize.defer(100,this, [w,h]); 
30496         //    return;
30497         //}
30498         
30499         
30500         
30501         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30502         
30503         this.frame.dom.setAttribute('width', w);
30504         this.frame.dom.setAttribute('height', h);
30505         this.frame.setSize(w,h);
30506         
30507     },
30508     
30509     toggleSourceEdit : function(value) {
30510         
30511       
30512          
30513         this.el.dom.style.display = value ? '' : 'none';
30514         this.frame.dom.style.display = value ?  'none' : '';
30515         
30516     },
30517     
30518     
30519     focus: function(tag)
30520     {
30521         if (this.frame.dom.style.display == 'none') {
30522             return Roo.form.FCKeditor.superclass.focus.call(this);
30523         }
30524         if(!this.el || !this.getEditor()) {
30525             this.focus.defer(100,this, [tag]); 
30526             return;
30527         }
30528         
30529         
30530         
30531         
30532         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
30533         this.getEditor().Focus();
30534         if (tgs.length) {
30535             if (!this.getEditor().Selection.GetSelection()) {
30536                 this.focus.defer(100,this, [tag]); 
30537                 return;
30538             }
30539             
30540             
30541             var r = this.getEditor().EditorDocument.createRange();
30542             r.setStart(tgs[0],0);
30543             r.setEnd(tgs[0],0);
30544             this.getEditor().Selection.GetSelection().removeAllRanges();
30545             this.getEditor().Selection.GetSelection().addRange(r);
30546             this.getEditor().Focus();
30547         }
30548         
30549     },
30550     
30551     
30552     
30553     replaceTextarea : function()
30554     {
30555         if ( document.getElementById( this.getId() + '___Frame' ) )
30556             return ;
30557         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
30558         //{
30559             // We must check the elements firstly using the Id and then the name.
30560         var oTextarea = document.getElementById( this.getId() );
30561         
30562         var colElementsByName = document.getElementsByName( this.getId() ) ;
30563          
30564         oTextarea.style.display = 'none' ;
30565
30566         if ( oTextarea.tabIndex ) {            
30567             this.TabIndex = oTextarea.tabIndex ;
30568         }
30569         
30570         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
30571         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
30572         this.frame = Roo.get(this.getId() + '___Frame')
30573     },
30574     
30575     _getConfigHtml : function()
30576     {
30577         var sConfig = '' ;
30578
30579         for ( var o in this.fckconfig ) {
30580             sConfig += sConfig.length > 0  ? '&amp;' : '';
30581             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
30582         }
30583
30584         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
30585     },
30586     
30587     
30588     _getIFrameHtml : function()
30589     {
30590         var sFile = 'fckeditor.html' ;
30591         /* no idea what this is about..
30592         try
30593         {
30594             if ( (/fcksource=true/i).test( window.top.location.search ) )
30595                 sFile = 'fckeditor.original.html' ;
30596         }
30597         catch (e) { 
30598         */
30599
30600         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
30601         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
30602         
30603         
30604         var html = '<iframe id="' + this.getId() +
30605             '___Frame" src="' + sLink +
30606             '" width="' + this.width +
30607             '" height="' + this.height + '"' +
30608             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
30609             ' frameborder="0" scrolling="no"></iframe>' ;
30610
30611         return html ;
30612     },
30613     
30614     _insertHtmlBefore : function( html, element )
30615     {
30616         if ( element.insertAdjacentHTML )       {
30617             // IE
30618             element.insertAdjacentHTML( 'beforeBegin', html ) ;
30619         } else { // Gecko
30620             var oRange = document.createRange() ;
30621             oRange.setStartBefore( element ) ;
30622             var oFragment = oRange.createContextualFragment( html );
30623             element.parentNode.insertBefore( oFragment, element ) ;
30624         }
30625     }
30626     
30627     
30628   
30629     
30630     
30631     
30632     
30633
30634 });
30635
30636 //Roo.reg('fckeditor', Roo.form.FCKeditor);
30637
30638 function FCKeditor_OnComplete(editorInstance){
30639     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
30640     f.fckEditor = editorInstance;
30641     //console.log("loaded");
30642     f.fireEvent('editorinit', f, editorInstance);
30643
30644   
30645
30646  
30647
30648
30649
30650
30651
30652
30653
30654
30655
30656
30657
30658
30659
30660
30661
30662 //<script type="text/javascript">
30663 /**
30664  * @class Roo.form.GridField
30665  * @extends Roo.form.Field
30666  * Embed a grid (or editable grid into a form)
30667  * STATUS ALPHA
30668  * 
30669  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
30670  * it needs 
30671  * xgrid.store = Roo.data.Store
30672  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
30673  * xgrid.store.reader = Roo.data.JsonReader 
30674  * 
30675  * 
30676  * @constructor
30677  * Creates a new GridField
30678  * @param {Object} config Configuration options
30679  */
30680 Roo.form.GridField = function(config){
30681     Roo.form.GridField.superclass.constructor.call(this, config);
30682      
30683 };
30684
30685 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
30686     /**
30687      * @cfg {Number} width  - used to restrict width of grid..
30688      */
30689     width : 100,
30690     /**
30691      * @cfg {Number} height - used to restrict height of grid..
30692      */
30693     height : 50,
30694      /**
30695      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
30696          * 
30697          *}
30698      */
30699     xgrid : false, 
30700     /**
30701      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30702      * {tag: "input", type: "checkbox", autocomplete: "off"})
30703      */
30704    // defaultAutoCreate : { tag: 'div' },
30705     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
30706     /**
30707      * @cfg {String} addTitle Text to include for adding a title.
30708      */
30709     addTitle : false,
30710     //
30711     onResize : function(){
30712         Roo.form.Field.superclass.onResize.apply(this, arguments);
30713     },
30714
30715     initEvents : function(){
30716         // Roo.form.Checkbox.superclass.initEvents.call(this);
30717         // has no events...
30718        
30719     },
30720
30721
30722     getResizeEl : function(){
30723         return this.wrap;
30724     },
30725
30726     getPositionEl : function(){
30727         return this.wrap;
30728     },
30729
30730     // private
30731     onRender : function(ct, position){
30732         
30733         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
30734         var style = this.style;
30735         delete this.style;
30736         
30737         Roo.form.GridField.superclass.onRender.call(this, ct, position);
30738         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
30739         this.viewEl = this.wrap.createChild({ tag: 'div' });
30740         if (style) {
30741             this.viewEl.applyStyles(style);
30742         }
30743         if (this.width) {
30744             this.viewEl.setWidth(this.width);
30745         }
30746         if (this.height) {
30747             this.viewEl.setHeight(this.height);
30748         }
30749         //if(this.inputValue !== undefined){
30750         //this.setValue(this.value);
30751         
30752         
30753         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
30754         
30755         
30756         this.grid.render();
30757         this.grid.getDataSource().on('remove', this.refreshValue, this);
30758         this.grid.getDataSource().on('update', this.refreshValue, this);
30759         this.grid.on('afteredit', this.refreshValue, this);
30760  
30761     },
30762      
30763     
30764     /**
30765      * Sets the value of the item. 
30766      * @param {String} either an object  or a string..
30767      */
30768     setValue : function(v){
30769         //this.value = v;
30770         v = v || []; // empty set..
30771         // this does not seem smart - it really only affects memoryproxy grids..
30772         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
30773             var ds = this.grid.getDataSource();
30774             // assumes a json reader..
30775             var data = {}
30776             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
30777             ds.loadData( data);
30778         }
30779         // clear selection so it does not get stale.
30780         if (this.grid.sm) { 
30781             this.grid.sm.clearSelections();
30782         }
30783         
30784         Roo.form.GridField.superclass.setValue.call(this, v);
30785         this.refreshValue();
30786         // should load data in the grid really....
30787     },
30788     
30789     // private
30790     refreshValue: function() {
30791          var val = [];
30792         this.grid.getDataSource().each(function(r) {
30793             val.push(r.data);
30794         });
30795         this.el.dom.value = Roo.encode(val);
30796     }
30797     
30798      
30799     
30800     
30801 });/*
30802  * Based on:
30803  * Ext JS Library 1.1.1
30804  * Copyright(c) 2006-2007, Ext JS, LLC.
30805  *
30806  * Originally Released Under LGPL - original licence link has changed is not relivant.
30807  *
30808  * Fork - LGPL
30809  * <script type="text/javascript">
30810  */
30811 /**
30812  * @class Roo.form.DisplayField
30813  * @extends Roo.form.Field
30814  * A generic Field to display non-editable data.
30815  * @constructor
30816  * Creates a new Display Field item.
30817  * @param {Object} config Configuration options
30818  */
30819 Roo.form.DisplayField = function(config){
30820     Roo.form.DisplayField.superclass.constructor.call(this, config);
30821     
30822 };
30823
30824 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
30825     inputType:      'hidden',
30826     allowBlank:     true,
30827     readOnly:         true,
30828     
30829  
30830     /**
30831      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30832      */
30833     focusClass : undefined,
30834     /**
30835      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30836      */
30837     fieldClass: 'x-form-field',
30838     
30839      /**
30840      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
30841      */
30842     valueRenderer: undefined,
30843     
30844     width: 100,
30845     /**
30846      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30847      * {tag: "input", type: "checkbox", autocomplete: "off"})
30848      */
30849      
30850  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30851
30852     onResize : function(){
30853         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
30854         
30855     },
30856
30857     initEvents : function(){
30858         // Roo.form.Checkbox.superclass.initEvents.call(this);
30859         // has no events...
30860        
30861     },
30862
30863
30864     getResizeEl : function(){
30865         return this.wrap;
30866     },
30867
30868     getPositionEl : function(){
30869         return this.wrap;
30870     },
30871
30872     // private
30873     onRender : function(ct, position){
30874         
30875         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
30876         //if(this.inputValue !== undefined){
30877         this.wrap = this.el.wrap();
30878         
30879         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
30880         
30881         if (this.bodyStyle) {
30882             this.viewEl.applyStyles(this.bodyStyle);
30883         }
30884         //this.viewEl.setStyle('padding', '2px');
30885         
30886         this.setValue(this.value);
30887         
30888     },
30889 /*
30890     // private
30891     initValue : Roo.emptyFn,
30892
30893   */
30894
30895         // private
30896     onClick : function(){
30897         
30898     },
30899
30900     /**
30901      * Sets the checked state of the checkbox.
30902      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
30903      */
30904     setValue : function(v){
30905         this.value = v;
30906         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
30907         // this might be called before we have a dom element..
30908         if (!this.viewEl) {
30909             return;
30910         }
30911         this.viewEl.dom.innerHTML = html;
30912         Roo.form.DisplayField.superclass.setValue.call(this, v);
30913
30914     }
30915 });/*
30916  * 
30917  * Licence- LGPL
30918  * 
30919  */
30920
30921 /**
30922  * @class Roo.form.DayPicker
30923  * @extends Roo.form.Field
30924  * A Day picker show [M] [T] [W] ....
30925  * @constructor
30926  * Creates a new Day Picker
30927  * @param {Object} config Configuration options
30928  */
30929 Roo.form.DayPicker= function(config){
30930     Roo.form.DayPicker.superclass.constructor.call(this, config);
30931      
30932 };
30933
30934 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
30935     /**
30936      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30937      */
30938     focusClass : undefined,
30939     /**
30940      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30941      */
30942     fieldClass: "x-form-field",
30943    
30944     /**
30945      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30946      * {tag: "input", type: "checkbox", autocomplete: "off"})
30947      */
30948     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
30949     
30950    
30951     actionMode : 'viewEl', 
30952     //
30953     // private
30954  
30955     inputType : 'hidden',
30956     
30957      
30958     inputElement: false, // real input element?
30959     basedOn: false, // ????
30960     
30961     isFormField: true, // not sure where this is needed!!!!
30962
30963     onResize : function(){
30964         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
30965         if(!this.boxLabel){
30966             this.el.alignTo(this.wrap, 'c-c');
30967         }
30968     },
30969
30970     initEvents : function(){
30971         Roo.form.Checkbox.superclass.initEvents.call(this);
30972         this.el.on("click", this.onClick,  this);
30973         this.el.on("change", this.onClick,  this);
30974     },
30975
30976
30977     getResizeEl : function(){
30978         return this.wrap;
30979     },
30980
30981     getPositionEl : function(){
30982         return this.wrap;
30983     },
30984
30985     
30986     // private
30987     onRender : function(ct, position){
30988         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
30989        
30990         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
30991         
30992         var r1 = '<table><tr>';
30993         var r2 = '<tr class="x-form-daypick-icons">';
30994         for (var i=0; i < 7; i++) {
30995             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
30996             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
30997         }
30998         
30999         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
31000         viewEl.select('img').on('click', this.onClick, this);
31001         this.viewEl = viewEl;   
31002         
31003         
31004         // this will not work on Chrome!!!
31005         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
31006         this.el.on('propertychange', this.setFromHidden,  this);  //ie
31007         
31008         
31009           
31010
31011     },
31012
31013     // private
31014     initValue : Roo.emptyFn,
31015
31016     /**
31017      * Returns the checked state of the checkbox.
31018      * @return {Boolean} True if checked, else false
31019      */
31020     getValue : function(){
31021         return this.el.dom.value;
31022         
31023     },
31024
31025         // private
31026     onClick : function(e){ 
31027         //this.setChecked(!this.checked);
31028         Roo.get(e.target).toggleClass('x-menu-item-checked');
31029         this.refreshValue();
31030         //if(this.el.dom.checked != this.checked){
31031         //    this.setValue(this.el.dom.checked);
31032        // }
31033     },
31034     
31035     // private
31036     refreshValue : function()
31037     {
31038         var val = '';
31039         this.viewEl.select('img',true).each(function(e,i,n)  {
31040             val += e.is(".x-menu-item-checked") ? String(n) : '';
31041         });
31042         this.setValue(val, true);
31043     },
31044
31045     /**
31046      * Sets the checked state of the checkbox.
31047      * On is always based on a string comparison between inputValue and the param.
31048      * @param {Boolean/String} value - the value to set 
31049      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
31050      */
31051     setValue : function(v,suppressEvent){
31052         if (!this.el.dom) {
31053             return;
31054         }
31055         var old = this.el.dom.value ;
31056         this.el.dom.value = v;
31057         if (suppressEvent) {
31058             return ;
31059         }
31060          
31061         // update display..
31062         this.viewEl.select('img',true).each(function(e,i,n)  {
31063             
31064             var on = e.is(".x-menu-item-checked");
31065             var newv = v.indexOf(String(n)) > -1;
31066             if (on != newv) {
31067                 e.toggleClass('x-menu-item-checked');
31068             }
31069             
31070         });
31071         
31072         
31073         this.fireEvent('change', this, v, old);
31074         
31075         
31076     },
31077    
31078     // handle setting of hidden value by some other method!!?!?
31079     setFromHidden: function()
31080     {
31081         if(!this.el){
31082             return;
31083         }
31084         //console.log("SET FROM HIDDEN");
31085         //alert('setFrom hidden');
31086         this.setValue(this.el.dom.value);
31087     },
31088     
31089     onDestroy : function()
31090     {
31091         if(this.viewEl){
31092             Roo.get(this.viewEl).remove();
31093         }
31094          
31095         Roo.form.DayPicker.superclass.onDestroy.call(this);
31096     }
31097
31098 });/*
31099  * RooJS Library 1.1.1
31100  * Copyright(c) 2008-2011  Alan Knowles
31101  *
31102  * License - LGPL
31103  */
31104  
31105
31106 /**
31107  * @class Roo.form.ComboCheck
31108  * @extends Roo.form.ComboBox
31109  * A combobox for multiple select items.
31110  *
31111  * FIXME - could do with a reset button..
31112  * 
31113  * @constructor
31114  * Create a new ComboCheck
31115  * @param {Object} config Configuration options
31116  */
31117 Roo.form.ComboCheck = function(config){
31118     Roo.form.ComboCheck.superclass.constructor.call(this, config);
31119     // should verify some data...
31120     // like
31121     // hiddenName = required..
31122     // displayField = required
31123     // valudField == required
31124     var req= [ 'hiddenName', 'displayField', 'valueField' ];
31125     var _t = this;
31126     Roo.each(req, function(e) {
31127         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
31128             throw "Roo.form.ComboCheck : missing value for: " + e;
31129         }
31130     });
31131     
31132     
31133 };
31134
31135 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
31136      
31137      
31138     editable : false,
31139      
31140     selectedClass: 'x-menu-item-checked', 
31141     
31142     // private
31143     onRender : function(ct, position){
31144         var _t = this;
31145         
31146         
31147         
31148         if(!this.tpl){
31149             var cls = 'x-combo-list';
31150
31151             
31152             this.tpl =  new Roo.Template({
31153                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
31154                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
31155                    '<span>{' + this.displayField + '}</span>' +
31156                     '</div>' 
31157                 
31158             });
31159         }
31160  
31161         
31162         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
31163         this.view.singleSelect = false;
31164         this.view.multiSelect = true;
31165         this.view.toggleSelect = true;
31166         this.pageTb.add(new Roo.Toolbar.Fill(), {
31167             
31168             text: 'Done',
31169             handler: function()
31170             {
31171                 _t.collapse();
31172             }
31173         });
31174     },
31175     
31176     onViewOver : function(e, t){
31177         // do nothing...
31178         return;
31179         
31180     },
31181     
31182     onViewClick : function(doFocus,index){
31183         return;
31184         
31185     },
31186     select: function () {
31187         //Roo.log("SELECT CALLED");
31188     },
31189      
31190     selectByValue : function(xv, scrollIntoView){
31191         var ar = this.getValueArray();
31192         var sels = [];
31193         
31194         Roo.each(ar, function(v) {
31195             if(v === undefined || v === null){
31196                 return;
31197             }
31198             var r = this.findRecord(this.valueField, v);
31199             if(r){
31200                 sels.push(this.store.indexOf(r))
31201                 
31202             }
31203         },this);
31204         this.view.select(sels);
31205         return false;
31206     },
31207     
31208     
31209     
31210     onSelect : function(record, index){
31211        // Roo.log("onselect Called");
31212        // this is only called by the clear button now..
31213         this.view.clearSelections();
31214         this.setValue('[]');
31215         if (this.value != this.valueBefore) {
31216             this.fireEvent('change', this, this.value, this.valueBefore);
31217             this.valueBefore = this.value;
31218         }
31219     },
31220     getValueArray : function()
31221     {
31222         var ar = [] ;
31223         
31224         try {
31225             //Roo.log(this.value);
31226             if (typeof(this.value) == 'undefined') {
31227                 return [];
31228             }
31229             var ar = Roo.decode(this.value);
31230             return  ar instanceof Array ? ar : []; //?? valid?
31231             
31232         } catch(e) {
31233             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
31234             return [];
31235         }
31236          
31237     },
31238     expand : function ()
31239     {
31240         
31241         Roo.form.ComboCheck.superclass.expand.call(this);
31242         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
31243         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
31244         
31245
31246     },
31247     
31248     collapse : function(){
31249         Roo.form.ComboCheck.superclass.collapse.call(this);
31250         var sl = this.view.getSelectedIndexes();
31251         var st = this.store;
31252         var nv = [];
31253         var tv = [];
31254         var r;
31255         Roo.each(sl, function(i) {
31256             r = st.getAt(i);
31257             nv.push(r.get(this.valueField));
31258         },this);
31259         this.setValue(Roo.encode(nv));
31260         if (this.value != this.valueBefore) {
31261
31262             this.fireEvent('change', this, this.value, this.valueBefore);
31263             this.valueBefore = this.value;
31264         }
31265         
31266     },
31267     
31268     setValue : function(v){
31269         // Roo.log(v);
31270         this.value = v;
31271         
31272         var vals = this.getValueArray();
31273         var tv = [];
31274         Roo.each(vals, function(k) {
31275             var r = this.findRecord(this.valueField, k);
31276             if(r){
31277                 tv.push(r.data[this.displayField]);
31278             }else if(this.valueNotFoundText !== undefined){
31279                 tv.push( this.valueNotFoundText );
31280             }
31281         },this);
31282        // Roo.log(tv);
31283         
31284         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
31285         this.hiddenField.value = v;
31286         this.value = v;
31287     }
31288     
31289 });/*
31290  * Based on:
31291  * Ext JS Library 1.1.1
31292  * Copyright(c) 2006-2007, Ext JS, LLC.
31293  *
31294  * Originally Released Under LGPL - original licence link has changed is not relivant.
31295  *
31296  * Fork - LGPL
31297  * <script type="text/javascript">
31298  */
31299  
31300 /**
31301  * @class Roo.form.Signature
31302  * @extends Roo.form.Field
31303  * Signature field.  
31304  * @constructor
31305  * 
31306  * @param {Object} config Configuration options
31307  */
31308
31309 Roo.form.Signature = function(config){
31310     Roo.form.Signature.superclass.constructor.call(this, config);
31311     
31312     this.addEvents({// not in used??
31313          /**
31314          * @event confirm
31315          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
31316              * @param {Roo.form.Signature} combo This combo box
31317              */
31318         'confirm' : true,
31319         /**
31320          * @event reset
31321          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
31322              * @param {Roo.form.ComboBox} combo This combo box
31323              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
31324              */
31325         'reset' : true
31326     });
31327 };
31328
31329 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
31330     /**
31331      * @cfg {Object} labels Label to use when rendering a form.
31332      * defaults to 
31333      * labels : { 
31334      *      clear : "Clear",
31335      *      confirm : "Confirm"
31336      *  }
31337      */
31338     labels : { 
31339         clear : "Clear",
31340         confirm : "Confirm"
31341     },
31342     /**
31343      * @cfg {Number} width The signature panel width (defaults to 300)
31344      */
31345     width: 300,
31346     /**
31347      * @cfg {Number} height The signature panel height (defaults to 100)
31348      */
31349     height : 100,
31350     /**
31351      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
31352      */
31353     allowBlank : false,
31354     
31355     //private
31356     // {Object} signPanel The signature SVG panel element (defaults to {})
31357     signPanel : {},
31358     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
31359     isMouseDown : false,
31360     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
31361     isConfirmed : false,
31362     // {String} signatureTmp SVG mapping string (defaults to empty string)
31363     signatureTmp : '',
31364     
31365     
31366     defaultAutoCreate : { // modified by initCompnoent..
31367         tag: "input",
31368         type:"hidden"
31369     },
31370
31371     // private
31372     onRender : function(ct, position){
31373         
31374         Roo.form.Signature.superclass.onRender.call(this, ct, position);
31375         
31376         this.wrap = this.el.wrap({
31377             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
31378         });
31379         
31380         this.createToolbar(this);
31381         this.signPanel = this.wrap.createChild({
31382                 tag: 'div',
31383                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
31384             }, this.el
31385         );
31386             
31387         this.svgID = Roo.id();
31388         this.svgEl = this.signPanel.createChild({
31389               xmlns : 'http://www.w3.org/2000/svg',
31390               tag : 'svg',
31391               id : this.svgID + "-svg",
31392               width: this.width,
31393               height: this.height,
31394               viewBox: '0 0 '+this.width+' '+this.height,
31395               cn : [
31396                 {
31397                     tag: "rect",
31398                     id: this.svgID + "-svg-r",
31399                     width: this.width,
31400                     height: this.height,
31401                     fill: "#ffa"
31402                 },
31403                 {
31404                     tag: "line",
31405                     id: this.svgID + "-svg-l",
31406                     x1: "0", // start
31407                     y1: (this.height*0.8), // start set the line in 80% of height
31408                     x2: this.width, // end
31409                     y2: (this.height*0.8), // end set the line in 80% of height
31410                     'stroke': "#666",
31411                     'stroke-width': "1",
31412                     'stroke-dasharray': "3",
31413                     'shape-rendering': "crispEdges",
31414                     'pointer-events': "none"
31415                 },
31416                 {
31417                     tag: "path",
31418                     id: this.svgID + "-svg-p",
31419                     'stroke': "navy",
31420                     'stroke-width': "3",
31421                     'fill': "none",
31422                     'pointer-events': 'none'
31423                 }
31424               ]
31425         });
31426         this.createSVG();
31427         this.svgBox = this.svgEl.dom.getScreenCTM();
31428     },
31429     createSVG : function(){ 
31430         var svg = this.signPanel;
31431         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
31432         var t = this;
31433
31434         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
31435         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
31436         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
31437         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
31438         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
31439         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
31440         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
31441         
31442     },
31443     isTouchEvent : function(e){
31444         return e.type.match(/^touch/);
31445     },
31446     getCoords : function (e) {
31447         var pt    = this.svgEl.dom.createSVGPoint();
31448         pt.x = e.clientX; 
31449         pt.y = e.clientY;
31450         if (this.isTouchEvent(e)) {
31451             pt.x =  e.targetTouches[0].clientX;
31452             pt.y = e.targetTouches[0].clientY;
31453         }
31454         var a = this.svgEl.dom.getScreenCTM();
31455         var b = a.inverse();
31456         var mx = pt.matrixTransform(b);
31457         return mx.x + ',' + mx.y;
31458     },
31459     //mouse event headler 
31460     down : function (e) {
31461         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
31462         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
31463         
31464         this.isMouseDown = true;
31465         
31466         e.preventDefault();
31467     },
31468     move : function (e) {
31469         if (this.isMouseDown) {
31470             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
31471             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
31472         }
31473         
31474         e.preventDefault();
31475     },
31476     up : function (e) {
31477         this.isMouseDown = false;
31478         var sp = this.signatureTmp.split(' ');
31479         
31480         if(sp.length > 1){
31481             if(!sp[sp.length-2].match(/^L/)){
31482                 sp.pop();
31483                 sp.pop();
31484                 sp.push("");
31485                 this.signatureTmp = sp.join(" ");
31486             }
31487         }
31488         if(this.getValue() != this.signatureTmp){
31489             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31490             this.isConfirmed = false;
31491         }
31492         e.preventDefault();
31493     },
31494     
31495     /**
31496      * Protected method that will not generally be called directly. It
31497      * is called when the editor creates its toolbar. Override this method if you need to
31498      * add custom toolbar buttons.
31499      * @param {HtmlEditor} editor
31500      */
31501     createToolbar : function(editor){
31502          function btn(id, toggle, handler){
31503             var xid = fid + '-'+ id ;
31504             return {
31505                 id : xid,
31506                 cmd : id,
31507                 cls : 'x-btn-icon x-edit-'+id,
31508                 enableToggle:toggle !== false,
31509                 scope: editor, // was editor...
31510                 handler:handler||editor.relayBtnCmd,
31511                 clickEvent:'mousedown',
31512                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
31513                 tabIndex:-1
31514             };
31515         }
31516         
31517         
31518         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
31519         this.tb = tb;
31520         this.tb.add(
31521            {
31522                 cls : ' x-signature-btn x-signature-'+id,
31523                 scope: editor, // was editor...
31524                 handler: this.reset,
31525                 clickEvent:'mousedown',
31526                 text: this.labels.clear
31527             },
31528             {
31529                  xtype : 'Fill',
31530                  xns: Roo.Toolbar
31531             }, 
31532             {
31533                 cls : '  x-signature-btn x-signature-'+id,
31534                 scope: editor, // was editor...
31535                 handler: this.confirmHandler,
31536                 clickEvent:'mousedown',
31537                 text: this.labels.confirm
31538             }
31539         );
31540     
31541     },
31542     //public
31543     /**
31544      * when user is clicked confirm then show this image.....
31545      * 
31546      * @return {String} Image Data URI
31547      */
31548     getImageDataURI : function(){
31549         var svg = this.svgEl.dom.parentNode.innerHTML;
31550         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
31551         return src; 
31552     },
31553     /**
31554      * 
31555      * @return {Boolean} this.isConfirmed
31556      */
31557     getConfirmed : function(){
31558         return this.isConfirmed;
31559     },
31560     /**
31561      * 
31562      * @return {Number} this.width
31563      */
31564     getWidth : function(){
31565         return this.width;
31566     },
31567     /**
31568      * 
31569      * @return {Number} this.height
31570      */
31571     getHeight : function(){
31572         return this.height;
31573     },
31574     // private
31575     getSignature : function(){
31576         return this.signatureTmp;
31577     },
31578     // private
31579     reset : function(){
31580         this.signatureTmp = '';
31581         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31582         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
31583         this.isConfirmed = false;
31584         Roo.form.Signature.superclass.reset.call(this);
31585     },
31586     setSignature : function(s){
31587         this.signatureTmp = s;
31588         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31589         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
31590         this.setValue(s);
31591         this.isConfirmed = false;
31592         Roo.form.Signature.superclass.reset.call(this);
31593     }, 
31594     test : function(){
31595 //        Roo.log(this.signPanel.dom.contentWindow.up())
31596     },
31597     //private
31598     setConfirmed : function(){
31599         
31600         
31601         
31602 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
31603     },
31604     // private
31605     confirmHandler : function(){
31606         if(!this.getSignature()){
31607             return;
31608         }
31609         
31610         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
31611         this.setValue(this.getSignature());
31612         this.isConfirmed = true;
31613         
31614         this.fireEvent('confirm', this);
31615     },
31616     // private
31617     // Subclasses should provide the validation implementation by overriding this
31618     validateValue : function(value){
31619         if(this.allowBlank){
31620             return true;
31621         }
31622         
31623         if(this.isConfirmed){
31624             return true;
31625         }
31626         return false;
31627     }
31628 });/*
31629  * Based on:
31630  * Ext JS Library 1.1.1
31631  * Copyright(c) 2006-2007, Ext JS, LLC.
31632  *
31633  * Originally Released Under LGPL - original licence link has changed is not relivant.
31634  *
31635  * Fork - LGPL
31636  * <script type="text/javascript">
31637  */
31638  
31639
31640 /**
31641  * @class Roo.form.ComboBox
31642  * @extends Roo.form.TriggerField
31643  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
31644  * @constructor
31645  * Create a new ComboBox.
31646  * @param {Object} config Configuration options
31647  */
31648 Roo.form.Select = function(config){
31649     Roo.form.Select.superclass.constructor.call(this, config);
31650      
31651 };
31652
31653 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
31654     /**
31655      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
31656      */
31657     /**
31658      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
31659      * rendering into an Roo.Editor, defaults to false)
31660      */
31661     /**
31662      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
31663      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
31664      */
31665     /**
31666      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
31667      */
31668     /**
31669      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
31670      * the dropdown list (defaults to undefined, with no header element)
31671      */
31672
31673      /**
31674      * @cfg {String/Roo.Template} tpl The template to use to render the output
31675      */
31676      
31677     // private
31678     defaultAutoCreate : {tag: "select"  },
31679     /**
31680      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
31681      */
31682     listWidth: undefined,
31683     /**
31684      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
31685      * mode = 'remote' or 'text' if mode = 'local')
31686      */
31687     displayField: undefined,
31688     /**
31689      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
31690      * mode = 'remote' or 'value' if mode = 'local'). 
31691      * Note: use of a valueField requires the user make a selection
31692      * in order for a value to be mapped.
31693      */
31694     valueField: undefined,
31695     
31696     
31697     /**
31698      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
31699      * field's data value (defaults to the underlying DOM element's name)
31700      */
31701     hiddenName: undefined,
31702     /**
31703      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
31704      */
31705     listClass: '',
31706     /**
31707      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
31708      */
31709     selectedClass: 'x-combo-selected',
31710     /**
31711      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
31712      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
31713      * which displays a downward arrow icon).
31714      */
31715     triggerClass : 'x-form-arrow-trigger',
31716     /**
31717      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31718      */
31719     shadow:'sides',
31720     /**
31721      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
31722      * anchor positions (defaults to 'tl-bl')
31723      */
31724     listAlign: 'tl-bl?',
31725     /**
31726      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
31727      */
31728     maxHeight: 300,
31729     /**
31730      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
31731      * query specified by the allQuery config option (defaults to 'query')
31732      */
31733     triggerAction: 'query',
31734     /**
31735      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
31736      * (defaults to 4, does not apply if editable = false)
31737      */
31738     minChars : 4,
31739     /**
31740      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
31741      * delay (typeAheadDelay) if it matches a known value (defaults to false)
31742      */
31743     typeAhead: false,
31744     /**
31745      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
31746      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
31747      */
31748     queryDelay: 500,
31749     /**
31750      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
31751      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
31752      */
31753     pageSize: 0,
31754     /**
31755      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
31756      * when editable = true (defaults to false)
31757      */
31758     selectOnFocus:false,
31759     /**
31760      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
31761      */
31762     queryParam: 'query',
31763     /**
31764      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
31765      * when mode = 'remote' (defaults to 'Loading...')
31766      */
31767     loadingText: 'Loading...',
31768     /**
31769      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
31770      */
31771     resizable: false,
31772     /**
31773      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
31774      */
31775     handleHeight : 8,
31776     /**
31777      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
31778      * traditional select (defaults to true)
31779      */
31780     editable: true,
31781     /**
31782      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
31783      */
31784     allQuery: '',
31785     /**
31786      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
31787      */
31788     mode: 'remote',
31789     /**
31790      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
31791      * listWidth has a higher value)
31792      */
31793     minListWidth : 70,
31794     /**
31795      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
31796      * allow the user to set arbitrary text into the field (defaults to false)
31797      */
31798     forceSelection:false,
31799     /**
31800      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
31801      * if typeAhead = true (defaults to 250)
31802      */
31803     typeAheadDelay : 250,
31804     /**
31805      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
31806      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
31807      */
31808     valueNotFoundText : undefined,
31809     
31810     /**
31811      * @cfg {String} defaultValue The value displayed after loading the store.
31812      */
31813     defaultValue: '',
31814     
31815     /**
31816      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
31817      */
31818     blockFocus : false,
31819     
31820     /**
31821      * @cfg {Boolean} disableClear Disable showing of clear button.
31822      */
31823     disableClear : false,
31824     /**
31825      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
31826      */
31827     alwaysQuery : false,
31828     
31829     //private
31830     addicon : false,
31831     editicon: false,
31832     
31833     // element that contains real text value.. (when hidden is used..)
31834      
31835     // private
31836     onRender : function(ct, position){
31837         Roo.form.Field.prototype.onRender.call(this, ct, position);
31838         
31839         if(this.store){
31840             this.store.on('beforeload', this.onBeforeLoad, this);
31841             this.store.on('load', this.onLoad, this);
31842             this.store.on('loadexception', this.onLoadException, this);
31843             this.store.load({});
31844         }
31845         
31846         
31847         
31848     },
31849
31850     // private
31851     initEvents : function(){
31852         //Roo.form.ComboBox.superclass.initEvents.call(this);
31853  
31854     },
31855
31856     onDestroy : function(){
31857        
31858         if(this.store){
31859             this.store.un('beforeload', this.onBeforeLoad, this);
31860             this.store.un('load', this.onLoad, this);
31861             this.store.un('loadexception', this.onLoadException, this);
31862         }
31863         //Roo.form.ComboBox.superclass.onDestroy.call(this);
31864     },
31865
31866     // private
31867     fireKey : function(e){
31868         if(e.isNavKeyPress() && !this.list.isVisible()){
31869             this.fireEvent("specialkey", this, e);
31870         }
31871     },
31872
31873     // private
31874     onResize: function(w, h){
31875         
31876         return; 
31877     
31878         
31879     },
31880
31881     /**
31882      * Allow or prevent the user from directly editing the field text.  If false is passed,
31883      * the user will only be able to select from the items defined in the dropdown list.  This method
31884      * is the runtime equivalent of setting the 'editable' config option at config time.
31885      * @param {Boolean} value True to allow the user to directly edit the field text
31886      */
31887     setEditable : function(value){
31888          
31889     },
31890
31891     // private
31892     onBeforeLoad : function(){
31893         
31894         Roo.log("Select before load");
31895         return;
31896     
31897         this.innerList.update(this.loadingText ?
31898                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
31899         //this.restrictHeight();
31900         this.selectedIndex = -1;
31901     },
31902
31903     // private
31904     onLoad : function(){
31905
31906     
31907         var dom = this.el.dom;
31908         dom.innerHTML = '';
31909          var od = dom.ownerDocument;
31910          
31911         if (this.emptyText) {
31912             var op = od.createElement('option');
31913             op.setAttribute('value', '');
31914             op.innerHTML = String.format('{0}', this.emptyText);
31915             dom.appendChild(op);
31916         }
31917         if(this.store.getCount() > 0){
31918            
31919             var vf = this.valueField;
31920             var df = this.displayField;
31921             this.store.data.each(function(r) {
31922                 // which colmsn to use... testing - cdoe / title..
31923                 var op = od.createElement('option');
31924                 op.setAttribute('value', r.data[vf]);
31925                 op.innerHTML = String.format('{0}', r.data[df]);
31926                 dom.appendChild(op);
31927             });
31928             if (typeof(this.defaultValue != 'undefined')) {
31929                 this.setValue(this.defaultValue);
31930             }
31931             
31932              
31933         }else{
31934             //this.onEmptyResults();
31935         }
31936         //this.el.focus();
31937     },
31938     // private
31939     onLoadException : function()
31940     {
31941         dom.innerHTML = '';
31942             
31943         Roo.log("Select on load exception");
31944         return;
31945     
31946         this.collapse();
31947         Roo.log(this.store.reader.jsonData);
31948         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
31949             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
31950         }
31951         
31952         
31953     },
31954     // private
31955     onTypeAhead : function(){
31956          
31957     },
31958
31959     // private
31960     onSelect : function(record, index){
31961         Roo.log('on select?');
31962         return;
31963         if(this.fireEvent('beforeselect', this, record, index) !== false){
31964             this.setFromData(index > -1 ? record.data : false);
31965             this.collapse();
31966             this.fireEvent('select', this, record, index);
31967         }
31968     },
31969
31970     /**
31971      * Returns the currently selected field value or empty string if no value is set.
31972      * @return {String} value The selected value
31973      */
31974     getValue : function(){
31975         var dom = this.el.dom;
31976         this.value = dom.options[dom.selectedIndex].value;
31977         return this.value;
31978         
31979     },
31980
31981     /**
31982      * Clears any text/value currently set in the field
31983      */
31984     clearValue : function(){
31985         this.value = '';
31986         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
31987         
31988     },
31989
31990     /**
31991      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
31992      * will be displayed in the field.  If the value does not match the data value of an existing item,
31993      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
31994      * Otherwise the field will be blank (although the value will still be set).
31995      * @param {String} value The value to match
31996      */
31997     setValue : function(v){
31998         var d = this.el.dom;
31999         for (var i =0; i < d.options.length;i++) {
32000             if (v == d.options[i].value) {
32001                 d.selectedIndex = i;
32002                 this.value = v;
32003                 return;
32004             }
32005         }
32006         this.clearValue();
32007     },
32008     /**
32009      * @property {Object} the last set data for the element
32010      */
32011     
32012     lastData : false,
32013     /**
32014      * Sets the value of the field based on a object which is related to the record format for the store.
32015      * @param {Object} value the value to set as. or false on reset?
32016      */
32017     setFromData : function(o){
32018         Roo.log('setfrom data?');
32019          
32020         
32021         
32022     },
32023     // private
32024     reset : function(){
32025         this.clearValue();
32026     },
32027     // private
32028     findRecord : function(prop, value){
32029         
32030         return false;
32031     
32032         var record;
32033         if(this.store.getCount() > 0){
32034             this.store.each(function(r){
32035                 if(r.data[prop] == value){
32036                     record = r;
32037                     return false;
32038                 }
32039                 return true;
32040             });
32041         }
32042         return record;
32043     },
32044     
32045     getName: function()
32046     {
32047         // returns hidden if it's set..
32048         if (!this.rendered) {return ''};
32049         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
32050         
32051     },
32052      
32053
32054     
32055
32056     // private
32057     onEmptyResults : function(){
32058         Roo.log('empty results');
32059         //this.collapse();
32060     },
32061
32062     /**
32063      * Returns true if the dropdown list is expanded, else false.
32064      */
32065     isExpanded : function(){
32066         return false;
32067     },
32068
32069     /**
32070      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
32071      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32072      * @param {String} value The data value of the item to select
32073      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32074      * selected item if it is not currently in view (defaults to true)
32075      * @return {Boolean} True if the value matched an item in the list, else false
32076      */
32077     selectByValue : function(v, scrollIntoView){
32078         Roo.log('select By Value');
32079         return false;
32080     
32081         if(v !== undefined && v !== null){
32082             var r = this.findRecord(this.valueField || this.displayField, v);
32083             if(r){
32084                 this.select(this.store.indexOf(r), scrollIntoView);
32085                 return true;
32086             }
32087         }
32088         return false;
32089     },
32090
32091     /**
32092      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
32093      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32094      * @param {Number} index The zero-based index of the list item to select
32095      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32096      * selected item if it is not currently in view (defaults to true)
32097      */
32098     select : function(index, scrollIntoView){
32099         Roo.log('select ');
32100         return  ;
32101         
32102         this.selectedIndex = index;
32103         this.view.select(index);
32104         if(scrollIntoView !== false){
32105             var el = this.view.getNode(index);
32106             if(el){
32107                 this.innerList.scrollChildIntoView(el, false);
32108             }
32109         }
32110     },
32111
32112       
32113
32114     // private
32115     validateBlur : function(){
32116         
32117         return;
32118         
32119     },
32120
32121     // private
32122     initQuery : function(){
32123         this.doQuery(this.getRawValue());
32124     },
32125
32126     // private
32127     doForce : function(){
32128         if(this.el.dom.value.length > 0){
32129             this.el.dom.value =
32130                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
32131              
32132         }
32133     },
32134
32135     /**
32136      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
32137      * query allowing the query action to be canceled if needed.
32138      * @param {String} query The SQL query to execute
32139      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
32140      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
32141      * saved in the current store (defaults to false)
32142      */
32143     doQuery : function(q, forceAll){
32144         
32145         Roo.log('doQuery?');
32146         if(q === undefined || q === null){
32147             q = '';
32148         }
32149         var qe = {
32150             query: q,
32151             forceAll: forceAll,
32152             combo: this,
32153             cancel:false
32154         };
32155         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
32156             return false;
32157         }
32158         q = qe.query;
32159         forceAll = qe.forceAll;
32160         if(forceAll === true || (q.length >= this.minChars)){
32161             if(this.lastQuery != q || this.alwaysQuery){
32162                 this.lastQuery = q;
32163                 if(this.mode == 'local'){
32164                     this.selectedIndex = -1;
32165                     if(forceAll){
32166                         this.store.clearFilter();
32167                     }else{
32168                         this.store.filter(this.displayField, q);
32169                     }
32170                     this.onLoad();
32171                 }else{
32172                     this.store.baseParams[this.queryParam] = q;
32173                     this.store.load({
32174                         params: this.getParams(q)
32175                     });
32176                     this.expand();
32177                 }
32178             }else{
32179                 this.selectedIndex = -1;
32180                 this.onLoad();   
32181             }
32182         }
32183     },
32184
32185     // private
32186     getParams : function(q){
32187         var p = {};
32188         //p[this.queryParam] = q;
32189         if(this.pageSize){
32190             p.start = 0;
32191             p.limit = this.pageSize;
32192         }
32193         return p;
32194     },
32195
32196     /**
32197      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
32198      */
32199     collapse : function(){
32200         
32201     },
32202
32203     // private
32204     collapseIf : function(e){
32205         
32206     },
32207
32208     /**
32209      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
32210      */
32211     expand : function(){
32212         
32213     } ,
32214
32215     // private
32216      
32217
32218     /** 
32219     * @cfg {Boolean} grow 
32220     * @hide 
32221     */
32222     /** 
32223     * @cfg {Number} growMin 
32224     * @hide 
32225     */
32226     /** 
32227     * @cfg {Number} growMax 
32228     * @hide 
32229     */
32230     /**
32231      * @hide
32232      * @method autoSize
32233      */
32234     
32235     setWidth : function()
32236     {
32237         
32238     },
32239     getResizeEl : function(){
32240         return this.el;
32241     }
32242 });//<script type="text/javasscript">
32243  
32244
32245 /**
32246  * @class Roo.DDView
32247  * A DnD enabled version of Roo.View.
32248  * @param {Element/String} container The Element in which to create the View.
32249  * @param {String} tpl The template string used to create the markup for each element of the View
32250  * @param {Object} config The configuration properties. These include all the config options of
32251  * {@link Roo.View} plus some specific to this class.<br>
32252  * <p>
32253  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
32254  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
32255  * <p>
32256  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
32257 .x-view-drag-insert-above {
32258         border-top:1px dotted #3366cc;
32259 }
32260 .x-view-drag-insert-below {
32261         border-bottom:1px dotted #3366cc;
32262 }
32263 </code></pre>
32264  * 
32265  */
32266  
32267 Roo.DDView = function(container, tpl, config) {
32268     Roo.DDView.superclass.constructor.apply(this, arguments);
32269     this.getEl().setStyle("outline", "0px none");
32270     this.getEl().unselectable();
32271     if (this.dragGroup) {
32272                 this.setDraggable(this.dragGroup.split(","));
32273     }
32274     if (this.dropGroup) {
32275                 this.setDroppable(this.dropGroup.split(","));
32276     }
32277     if (this.deletable) {
32278         this.setDeletable();
32279     }
32280     this.isDirtyFlag = false;
32281         this.addEvents({
32282                 "drop" : true
32283         });
32284 };
32285
32286 Roo.extend(Roo.DDView, Roo.View, {
32287 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
32288 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
32289 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
32290 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
32291
32292         isFormField: true,
32293
32294         reset: Roo.emptyFn,
32295         
32296         clearInvalid: Roo.form.Field.prototype.clearInvalid,
32297
32298         validate: function() {
32299                 return true;
32300         },
32301         
32302         destroy: function() {
32303                 this.purgeListeners();
32304                 this.getEl.removeAllListeners();
32305                 this.getEl().remove();
32306                 if (this.dragZone) {
32307                         if (this.dragZone.destroy) {
32308                                 this.dragZone.destroy();
32309                         }
32310                 }
32311                 if (this.dropZone) {
32312                         if (this.dropZone.destroy) {
32313                                 this.dropZone.destroy();
32314                         }
32315                 }
32316         },
32317
32318 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
32319         getName: function() {
32320                 return this.name;
32321         },
32322
32323 /**     Loads the View from a JSON string representing the Records to put into the Store. */
32324         setValue: function(v) {
32325                 if (!this.store) {
32326                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
32327                 }
32328                 var data = {};
32329                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
32330                 this.store.proxy = new Roo.data.MemoryProxy(data);
32331                 this.store.load();
32332         },
32333
32334 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
32335         getValue: function() {
32336                 var result = '(';
32337                 this.store.each(function(rec) {
32338                         result += rec.id + ',';
32339                 });
32340                 return result.substr(0, result.length - 1) + ')';
32341         },
32342         
32343         getIds: function() {
32344                 var i = 0, result = new Array(this.store.getCount());
32345                 this.store.each(function(rec) {
32346                         result[i++] = rec.id;
32347                 });
32348                 return result;
32349         },
32350         
32351         isDirty: function() {
32352                 return this.isDirtyFlag;
32353         },
32354
32355 /**
32356  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
32357  *      whole Element becomes the target, and this causes the drop gesture to append.
32358  */
32359     getTargetFromEvent : function(e) {
32360                 var target = e.getTarget();
32361                 while ((target !== null) && (target.parentNode != this.el.dom)) {
32362                 target = target.parentNode;
32363                 }
32364                 if (!target) {
32365                         target = this.el.dom.lastChild || this.el.dom;
32366                 }
32367                 return target;
32368     },
32369
32370 /**
32371  *      Create the drag data which consists of an object which has the property "ddel" as
32372  *      the drag proxy element. 
32373  */
32374     getDragData : function(e) {
32375         var target = this.findItemFromChild(e.getTarget());
32376                 if(target) {
32377                         this.handleSelection(e);
32378                         var selNodes = this.getSelectedNodes();
32379             var dragData = {
32380                 source: this,
32381                 copy: this.copy || (this.allowCopy && e.ctrlKey),
32382                 nodes: selNodes,
32383                 records: []
32384                         };
32385                         var selectedIndices = this.getSelectedIndexes();
32386                         for (var i = 0; i < selectedIndices.length; i++) {
32387                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
32388                         }
32389                         if (selNodes.length == 1) {
32390                                 dragData.ddel = target.cloneNode(true); // the div element
32391                         } else {
32392                                 var div = document.createElement('div'); // create the multi element drag "ghost"
32393                                 div.className = 'multi-proxy';
32394                                 for (var i = 0, len = selNodes.length; i < len; i++) {
32395                                         div.appendChild(selNodes[i].cloneNode(true));
32396                                 }
32397                                 dragData.ddel = div;
32398                         }
32399             //console.log(dragData)
32400             //console.log(dragData.ddel.innerHTML)
32401                         return dragData;
32402                 }
32403         //console.log('nodragData')
32404                 return false;
32405     },
32406     
32407 /**     Specify to which ddGroup items in this DDView may be dragged. */
32408     setDraggable: function(ddGroup) {
32409         if (ddGroup instanceof Array) {
32410                 Roo.each(ddGroup, this.setDraggable, this);
32411                 return;
32412         }
32413         if (this.dragZone) {
32414                 this.dragZone.addToGroup(ddGroup);
32415         } else {
32416                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
32417                                 containerScroll: true,
32418                                 ddGroup: ddGroup 
32419
32420                         });
32421 //                      Draggability implies selection. DragZone's mousedown selects the element.
32422                         if (!this.multiSelect) { this.singleSelect = true; }
32423
32424 //                      Wire the DragZone's handlers up to methods in *this*
32425                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
32426                 }
32427     },
32428
32429 /**     Specify from which ddGroup this DDView accepts drops. */
32430     setDroppable: function(ddGroup) {
32431         if (ddGroup instanceof Array) {
32432                 Roo.each(ddGroup, this.setDroppable, this);
32433                 return;
32434         }
32435         if (this.dropZone) {
32436                 this.dropZone.addToGroup(ddGroup);
32437         } else {
32438                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
32439                                 containerScroll: true,
32440                                 ddGroup: ddGroup
32441                         });
32442
32443 //                      Wire the DropZone's handlers up to methods in *this*
32444                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
32445                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
32446                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
32447                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
32448                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
32449                 }
32450     },
32451
32452 /**     Decide whether to drop above or below a View node. */
32453     getDropPoint : function(e, n, dd){
32454         if (n == this.el.dom) { return "above"; }
32455                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
32456                 var c = t + (b - t) / 2;
32457                 var y = Roo.lib.Event.getPageY(e);
32458                 if(y <= c) {
32459                         return "above";
32460                 }else{
32461                         return "below";
32462                 }
32463     },
32464
32465     onNodeEnter : function(n, dd, e, data){
32466                 return false;
32467     },
32468     
32469     onNodeOver : function(n, dd, e, data){
32470                 var pt = this.getDropPoint(e, n, dd);
32471                 // set the insert point style on the target node
32472                 var dragElClass = this.dropNotAllowed;
32473                 if (pt) {
32474                         var targetElClass;
32475                         if (pt == "above"){
32476                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
32477                                 targetElClass = "x-view-drag-insert-above";
32478                         } else {
32479                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
32480                                 targetElClass = "x-view-drag-insert-below";
32481                         }
32482                         if (this.lastInsertClass != targetElClass){
32483                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
32484                                 this.lastInsertClass = targetElClass;
32485                         }
32486                 }
32487                 return dragElClass;
32488         },
32489
32490     onNodeOut : function(n, dd, e, data){
32491                 this.removeDropIndicators(n);
32492     },
32493
32494     onNodeDrop : function(n, dd, e, data){
32495         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
32496                 return false;
32497         }
32498         var pt = this.getDropPoint(e, n, dd);
32499                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
32500                 if (pt == "below") { insertAt++; }
32501                 for (var i = 0; i < data.records.length; i++) {
32502                         var r = data.records[i];
32503                         var dup = this.store.getById(r.id);
32504                         if (dup && (dd != this.dragZone)) {
32505                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
32506                         } else {
32507                                 if (data.copy) {
32508                                         this.store.insert(insertAt++, r.copy());
32509                                 } else {
32510                                         data.source.isDirtyFlag = true;
32511                                         r.store.remove(r);
32512                                         this.store.insert(insertAt++, r);
32513                                 }
32514                                 this.isDirtyFlag = true;
32515                         }
32516                 }
32517                 this.dragZone.cachedTarget = null;
32518                 return true;
32519     },
32520
32521     removeDropIndicators : function(n){
32522                 if(n){
32523                         Roo.fly(n).removeClass([
32524                                 "x-view-drag-insert-above",
32525                                 "x-view-drag-insert-below"]);
32526                         this.lastInsertClass = "_noclass";
32527                 }
32528     },
32529
32530 /**
32531  *      Utility method. Add a delete option to the DDView's context menu.
32532  *      @param {String} imageUrl The URL of the "delete" icon image.
32533  */
32534         setDeletable: function(imageUrl) {
32535                 if (!this.singleSelect && !this.multiSelect) {
32536                         this.singleSelect = true;
32537                 }
32538                 var c = this.getContextMenu();
32539                 this.contextMenu.on("itemclick", function(item) {
32540                         switch (item.id) {
32541                                 case "delete":
32542                                         this.remove(this.getSelectedIndexes());
32543                                         break;
32544                         }
32545                 }, this);
32546                 this.contextMenu.add({
32547                         icon: imageUrl,
32548                         id: "delete",
32549                         text: 'Delete'
32550                 });
32551         },
32552         
32553 /**     Return the context menu for this DDView. */
32554         getContextMenu: function() {
32555                 if (!this.contextMenu) {
32556 //                      Create the View's context menu
32557                         this.contextMenu = new Roo.menu.Menu({
32558                                 id: this.id + "-contextmenu"
32559                         });
32560                         this.el.on("contextmenu", this.showContextMenu, this);
32561                 }
32562                 return this.contextMenu;
32563         },
32564         
32565         disableContextMenu: function() {
32566                 if (this.contextMenu) {
32567                         this.el.un("contextmenu", this.showContextMenu, this);
32568                 }
32569         },
32570
32571         showContextMenu: function(e, item) {
32572         item = this.findItemFromChild(e.getTarget());
32573                 if (item) {
32574                         e.stopEvent();
32575                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
32576                         this.contextMenu.showAt(e.getXY());
32577             }
32578     },
32579
32580 /**
32581  *      Remove {@link Roo.data.Record}s at the specified indices.
32582  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
32583  */
32584     remove: function(selectedIndices) {
32585                 selectedIndices = [].concat(selectedIndices);
32586                 for (var i = 0; i < selectedIndices.length; i++) {
32587                         var rec = this.store.getAt(selectedIndices[i]);
32588                         this.store.remove(rec);
32589                 }
32590     },
32591
32592 /**
32593  *      Double click fires the event, but also, if this is draggable, and there is only one other
32594  *      related DropZone, it transfers the selected node.
32595  */
32596     onDblClick : function(e){
32597         var item = this.findItemFromChild(e.getTarget());
32598         if(item){
32599             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
32600                 return false;
32601             }
32602             if (this.dragGroup) {
32603                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
32604                     while (targets.indexOf(this.dropZone) > -1) {
32605                             targets.remove(this.dropZone);
32606                                 }
32607                     if (targets.length == 1) {
32608                                         this.dragZone.cachedTarget = null;
32609                         var el = Roo.get(targets[0].getEl());
32610                         var box = el.getBox(true);
32611                         targets[0].onNodeDrop(el.dom, {
32612                                 target: el.dom,
32613                                 xy: [box.x, box.y + box.height - 1]
32614                         }, null, this.getDragData(e));
32615                     }
32616                 }
32617         }
32618     },
32619     
32620     handleSelection: function(e) {
32621                 this.dragZone.cachedTarget = null;
32622         var item = this.findItemFromChild(e.getTarget());
32623         if (!item) {
32624                 this.clearSelections(true);
32625                 return;
32626         }
32627                 if (item && (this.multiSelect || this.singleSelect)){
32628                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
32629                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
32630                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
32631                                 this.unselect(item);
32632                         } else {
32633                                 this.select(item, this.multiSelect && e.ctrlKey);
32634                                 this.lastSelection = item;
32635                         }
32636                 }
32637     },
32638
32639     onItemClick : function(item, index, e){
32640                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
32641                         return false;
32642                 }
32643                 return true;
32644     },
32645
32646     unselect : function(nodeInfo, suppressEvent){
32647                 var node = this.getNode(nodeInfo);
32648                 if(node && this.isSelected(node)){
32649                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
32650                                 Roo.fly(node).removeClass(this.selectedClass);
32651                                 this.selections.remove(node);
32652                                 if(!suppressEvent){
32653                                         this.fireEvent("selectionchange", this, this.selections);
32654                                 }
32655                         }
32656                 }
32657     }
32658 });
32659 /*
32660  * Based on:
32661  * Ext JS Library 1.1.1
32662  * Copyright(c) 2006-2007, Ext JS, LLC.
32663  *
32664  * Originally Released Under LGPL - original licence link has changed is not relivant.
32665  *
32666  * Fork - LGPL
32667  * <script type="text/javascript">
32668  */
32669  
32670 /**
32671  * @class Roo.LayoutManager
32672  * @extends Roo.util.Observable
32673  * Base class for layout managers.
32674  */
32675 Roo.LayoutManager = function(container, config){
32676     Roo.LayoutManager.superclass.constructor.call(this);
32677     this.el = Roo.get(container);
32678     // ie scrollbar fix
32679     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32680         document.body.scroll = "no";
32681     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32682         this.el.position('relative');
32683     }
32684     this.id = this.el.id;
32685     this.el.addClass("x-layout-container");
32686     /** false to disable window resize monitoring @type Boolean */
32687     this.monitorWindowResize = true;
32688     this.regions = {};
32689     this.addEvents({
32690         /**
32691          * @event layout
32692          * Fires when a layout is performed. 
32693          * @param {Roo.LayoutManager} this
32694          */
32695         "layout" : true,
32696         /**
32697          * @event regionresized
32698          * Fires when the user resizes a region. 
32699          * @param {Roo.LayoutRegion} region The resized region
32700          * @param {Number} newSize The new size (width for east/west, height for north/south)
32701          */
32702         "regionresized" : true,
32703         /**
32704          * @event regioncollapsed
32705          * Fires when a region is collapsed. 
32706          * @param {Roo.LayoutRegion} region The collapsed region
32707          */
32708         "regioncollapsed" : true,
32709         /**
32710          * @event regionexpanded
32711          * Fires when a region is expanded.  
32712          * @param {Roo.LayoutRegion} region The expanded region
32713          */
32714         "regionexpanded" : true
32715     });
32716     this.updating = false;
32717     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32718 };
32719
32720 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
32721     /**
32722      * Returns true if this layout is currently being updated
32723      * @return {Boolean}
32724      */
32725     isUpdating : function(){
32726         return this.updating; 
32727     },
32728     
32729     /**
32730      * Suspend the LayoutManager from doing auto-layouts while
32731      * making multiple add or remove calls
32732      */
32733     beginUpdate : function(){
32734         this.updating = true;    
32735     },
32736     
32737     /**
32738      * Restore auto-layouts and optionally disable the manager from performing a layout
32739      * @param {Boolean} noLayout true to disable a layout update 
32740      */
32741     endUpdate : function(noLayout){
32742         this.updating = false;
32743         if(!noLayout){
32744             this.layout();
32745         }    
32746     },
32747     
32748     layout: function(){
32749         
32750     },
32751     
32752     onRegionResized : function(region, newSize){
32753         this.fireEvent("regionresized", region, newSize);
32754         this.layout();
32755     },
32756     
32757     onRegionCollapsed : function(region){
32758         this.fireEvent("regioncollapsed", region);
32759     },
32760     
32761     onRegionExpanded : function(region){
32762         this.fireEvent("regionexpanded", region);
32763     },
32764         
32765     /**
32766      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32767      * performs box-model adjustments.
32768      * @return {Object} The size as an object {width: (the width), height: (the height)}
32769      */
32770     getViewSize : function(){
32771         var size;
32772         if(this.el.dom != document.body){
32773             size = this.el.getSize();
32774         }else{
32775             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32776         }
32777         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32778         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32779         return size;
32780     },
32781     
32782     /**
32783      * Returns the Element this layout is bound to.
32784      * @return {Roo.Element}
32785      */
32786     getEl : function(){
32787         return this.el;
32788     },
32789     
32790     /**
32791      * Returns the specified region.
32792      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32793      * @return {Roo.LayoutRegion}
32794      */
32795     getRegion : function(target){
32796         return this.regions[target.toLowerCase()];
32797     },
32798     
32799     onWindowResize : function(){
32800         if(this.monitorWindowResize){
32801             this.layout();
32802         }
32803     }
32804 });/*
32805  * Based on:
32806  * Ext JS Library 1.1.1
32807  * Copyright(c) 2006-2007, Ext JS, LLC.
32808  *
32809  * Originally Released Under LGPL - original licence link has changed is not relivant.
32810  *
32811  * Fork - LGPL
32812  * <script type="text/javascript">
32813  */
32814 /**
32815  * @class Roo.BorderLayout
32816  * @extends Roo.LayoutManager
32817  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32818  * please see: <br><br>
32819  * <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>
32820  * <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>
32821  * Example:
32822  <pre><code>
32823  var layout = new Roo.BorderLayout(document.body, {
32824     north: {
32825         initialSize: 25,
32826         titlebar: false
32827     },
32828     west: {
32829         split:true,
32830         initialSize: 200,
32831         minSize: 175,
32832         maxSize: 400,
32833         titlebar: true,
32834         collapsible: true
32835     },
32836     east: {
32837         split:true,
32838         initialSize: 202,
32839         minSize: 175,
32840         maxSize: 400,
32841         titlebar: true,
32842         collapsible: true
32843     },
32844     south: {
32845         split:true,
32846         initialSize: 100,
32847         minSize: 100,
32848         maxSize: 200,
32849         titlebar: true,
32850         collapsible: true
32851     },
32852     center: {
32853         titlebar: true,
32854         autoScroll:true,
32855         resizeTabs: true,
32856         minTabWidth: 50,
32857         preferredTabWidth: 150
32858     }
32859 });
32860
32861 // shorthand
32862 var CP = Roo.ContentPanel;
32863
32864 layout.beginUpdate();
32865 layout.add("north", new CP("north", "North"));
32866 layout.add("south", new CP("south", {title: "South", closable: true}));
32867 layout.add("west", new CP("west", {title: "West"}));
32868 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
32869 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
32870 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
32871 layout.getRegion("center").showPanel("center1");
32872 layout.endUpdate();
32873 </code></pre>
32874
32875 <b>The container the layout is rendered into can be either the body element or any other element.
32876 If it is not the body element, the container needs to either be an absolute positioned element,
32877 or you will need to add "position:relative" to the css of the container.  You will also need to specify
32878 the container size if it is not the body element.</b>
32879
32880 * @constructor
32881 * Create a new BorderLayout
32882 * @param {String/HTMLElement/Element} container The container this layout is bound to
32883 * @param {Object} config Configuration options
32884  */
32885 Roo.BorderLayout = function(container, config){
32886     config = config || {};
32887     Roo.BorderLayout.superclass.constructor.call(this, container, config);
32888     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
32889     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
32890         var target = this.factory.validRegions[i];
32891         if(config[target]){
32892             this.addRegion(target, config[target]);
32893         }
32894     }
32895 };
32896
32897 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
32898     /**
32899      * Creates and adds a new region if it doesn't already exist.
32900      * @param {String} target The target region key (north, south, east, west or center).
32901      * @param {Object} config The regions config object
32902      * @return {BorderLayoutRegion} The new region
32903      */
32904     addRegion : function(target, config){
32905         if(!this.regions[target]){
32906             var r = this.factory.create(target, this, config);
32907             this.bindRegion(target, r);
32908         }
32909         return this.regions[target];
32910     },
32911
32912     // private (kinda)
32913     bindRegion : function(name, r){
32914         this.regions[name] = r;
32915         r.on("visibilitychange", this.layout, this);
32916         r.on("paneladded", this.layout, this);
32917         r.on("panelremoved", this.layout, this);
32918         r.on("invalidated", this.layout, this);
32919         r.on("resized", this.onRegionResized, this);
32920         r.on("collapsed", this.onRegionCollapsed, this);
32921         r.on("expanded", this.onRegionExpanded, this);
32922     },
32923
32924     /**
32925      * Performs a layout update.
32926      */
32927     layout : function(){
32928         if(this.updating) return;
32929         var size = this.getViewSize();
32930         var w = size.width;
32931         var h = size.height;
32932         var centerW = w;
32933         var centerH = h;
32934         var centerY = 0;
32935         var centerX = 0;
32936         //var x = 0, y = 0;
32937
32938         var rs = this.regions;
32939         var north = rs["north"];
32940         var south = rs["south"]; 
32941         var west = rs["west"];
32942         var east = rs["east"];
32943         var center = rs["center"];
32944         //if(this.hideOnLayout){ // not supported anymore
32945             //c.el.setStyle("display", "none");
32946         //}
32947         if(north && north.isVisible()){
32948             var b = north.getBox();
32949             var m = north.getMargins();
32950             b.width = w - (m.left+m.right);
32951             b.x = m.left;
32952             b.y = m.top;
32953             centerY = b.height + b.y + m.bottom;
32954             centerH -= centerY;
32955             north.updateBox(this.safeBox(b));
32956         }
32957         if(south && south.isVisible()){
32958             var b = south.getBox();
32959             var m = south.getMargins();
32960             b.width = w - (m.left+m.right);
32961             b.x = m.left;
32962             var totalHeight = (b.height + m.top + m.bottom);
32963             b.y = h - totalHeight + m.top;
32964             centerH -= totalHeight;
32965             south.updateBox(this.safeBox(b));
32966         }
32967         if(west && west.isVisible()){
32968             var b = west.getBox();
32969             var m = west.getMargins();
32970             b.height = centerH - (m.top+m.bottom);
32971             b.x = m.left;
32972             b.y = centerY + m.top;
32973             var totalWidth = (b.width + m.left + m.right);
32974             centerX += totalWidth;
32975             centerW -= totalWidth;
32976             west.updateBox(this.safeBox(b));
32977         }
32978         if(east && east.isVisible()){
32979             var b = east.getBox();
32980             var m = east.getMargins();
32981             b.height = centerH - (m.top+m.bottom);
32982             var totalWidth = (b.width + m.left + m.right);
32983             b.x = w - totalWidth + m.left;
32984             b.y = centerY + m.top;
32985             centerW -= totalWidth;
32986             east.updateBox(this.safeBox(b));
32987         }
32988         if(center){
32989             var m = center.getMargins();
32990             var centerBox = {
32991                 x: centerX + m.left,
32992                 y: centerY + m.top,
32993                 width: centerW - (m.left+m.right),
32994                 height: centerH - (m.top+m.bottom)
32995             };
32996             //if(this.hideOnLayout){
32997                 //center.el.setStyle("display", "block");
32998             //}
32999             center.updateBox(this.safeBox(centerBox));
33000         }
33001         this.el.repaint();
33002         this.fireEvent("layout", this);
33003     },
33004
33005     // private
33006     safeBox : function(box){
33007         box.width = Math.max(0, box.width);
33008         box.height = Math.max(0, box.height);
33009         return box;
33010     },
33011
33012     /**
33013      * Adds a ContentPanel (or subclass) to this layout.
33014      * @param {String} target The target region key (north, south, east, west or center).
33015      * @param {Roo.ContentPanel} panel The panel to add
33016      * @return {Roo.ContentPanel} The added panel
33017      */
33018     add : function(target, panel){
33019          
33020         target = target.toLowerCase();
33021         return this.regions[target].add(panel);
33022     },
33023
33024     /**
33025      * Remove a ContentPanel (or subclass) to this layout.
33026      * @param {String} target The target region key (north, south, east, west or center).
33027      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
33028      * @return {Roo.ContentPanel} The removed panel
33029      */
33030     remove : function(target, panel){
33031         target = target.toLowerCase();
33032         return this.regions[target].remove(panel);
33033     },
33034
33035     /**
33036      * Searches all regions for a panel with the specified id
33037      * @param {String} panelId
33038      * @return {Roo.ContentPanel} The panel or null if it wasn't found
33039      */
33040     findPanel : function(panelId){
33041         var rs = this.regions;
33042         for(var target in rs){
33043             if(typeof rs[target] != "function"){
33044                 var p = rs[target].getPanel(panelId);
33045                 if(p){
33046                     return p;
33047                 }
33048             }
33049         }
33050         return null;
33051     },
33052
33053     /**
33054      * Searches all regions for a panel with the specified id and activates (shows) it.
33055      * @param {String/ContentPanel} panelId The panels id or the panel itself
33056      * @return {Roo.ContentPanel} The shown panel or null
33057      */
33058     showPanel : function(panelId) {
33059       var rs = this.regions;
33060       for(var target in rs){
33061          var r = rs[target];
33062          if(typeof r != "function"){
33063             if(r.hasPanel(panelId)){
33064                return r.showPanel(panelId);
33065             }
33066          }
33067       }
33068       return null;
33069    },
33070
33071    /**
33072      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
33073      * @param {Roo.state.Provider} provider (optional) An alternate state provider
33074      */
33075     restoreState : function(provider){
33076         if(!provider){
33077             provider = Roo.state.Manager;
33078         }
33079         var sm = new Roo.LayoutStateManager();
33080         sm.init(this, provider);
33081     },
33082
33083     /**
33084      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
33085      * object should contain properties for each region to add ContentPanels to, and each property's value should be
33086      * a valid ContentPanel config object.  Example:
33087      * <pre><code>
33088 // Create the main layout
33089 var layout = new Roo.BorderLayout('main-ct', {
33090     west: {
33091         split:true,
33092         minSize: 175,
33093         titlebar: true
33094     },
33095     center: {
33096         title:'Components'
33097     }
33098 }, 'main-ct');
33099
33100 // Create and add multiple ContentPanels at once via configs
33101 layout.batchAdd({
33102    west: {
33103        id: 'source-files',
33104        autoCreate:true,
33105        title:'Ext Source Files',
33106        autoScroll:true,
33107        fitToFrame:true
33108    },
33109    center : {
33110        el: cview,
33111        autoScroll:true,
33112        fitToFrame:true,
33113        toolbar: tb,
33114        resizeEl:'cbody'
33115    }
33116 });
33117 </code></pre>
33118      * @param {Object} regions An object containing ContentPanel configs by region name
33119      */
33120     batchAdd : function(regions){
33121         this.beginUpdate();
33122         for(var rname in regions){
33123             var lr = this.regions[rname];
33124             if(lr){
33125                 this.addTypedPanels(lr, regions[rname]);
33126             }
33127         }
33128         this.endUpdate();
33129     },
33130
33131     // private
33132     addTypedPanels : function(lr, ps){
33133         if(typeof ps == 'string'){
33134             lr.add(new Roo.ContentPanel(ps));
33135         }
33136         else if(ps instanceof Array){
33137             for(var i =0, len = ps.length; i < len; i++){
33138                 this.addTypedPanels(lr, ps[i]);
33139             }
33140         }
33141         else if(!ps.events){ // raw config?
33142             var el = ps.el;
33143             delete ps.el; // prevent conflict
33144             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
33145         }
33146         else {  // panel object assumed!
33147             lr.add(ps);
33148         }
33149     },
33150     /**
33151      * Adds a xtype elements to the layout.
33152      * <pre><code>
33153
33154 layout.addxtype({
33155        xtype : 'ContentPanel',
33156        region: 'west',
33157        items: [ .... ]
33158    }
33159 );
33160
33161 layout.addxtype({
33162         xtype : 'NestedLayoutPanel',
33163         region: 'west',
33164         layout: {
33165            center: { },
33166            west: { }   
33167         },
33168         items : [ ... list of content panels or nested layout panels.. ]
33169    }
33170 );
33171 </code></pre>
33172      * @param {Object} cfg Xtype definition of item to add.
33173      */
33174     addxtype : function(cfg)
33175     {
33176         // basically accepts a pannel...
33177         // can accept a layout region..!?!?
33178         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
33179         
33180         if (!cfg.xtype.match(/Panel$/)) {
33181             return false;
33182         }
33183         var ret = false;
33184         
33185         if (typeof(cfg.region) == 'undefined') {
33186             Roo.log("Failed to add Panel, region was not set");
33187             Roo.log(cfg);
33188             return false;
33189         }
33190         var region = cfg.region;
33191         delete cfg.region;
33192         
33193           
33194         var xitems = [];
33195         if (cfg.items) {
33196             xitems = cfg.items;
33197             delete cfg.items;
33198         }
33199         var nb = false;
33200         
33201         switch(cfg.xtype) 
33202         {
33203             case 'ContentPanel':  // ContentPanel (el, cfg)
33204             case 'ScrollPanel':  // ContentPanel (el, cfg)
33205             case 'ViewPanel': 
33206                 if(cfg.autoCreate) {
33207                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33208                 } else {
33209                     var el = this.el.createChild();
33210                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
33211                 }
33212                 
33213                 this.add(region, ret);
33214                 break;
33215             
33216             
33217             case 'TreePanel': // our new panel!
33218                 cfg.el = this.el.createChild();
33219                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33220                 this.add(region, ret);
33221                 break;
33222             
33223             case 'NestedLayoutPanel': 
33224                 // create a new Layout (which is  a Border Layout...
33225                 var el = this.el.createChild();
33226                 var clayout = cfg.layout;
33227                 delete cfg.layout;
33228                 clayout.items   = clayout.items  || [];
33229                 // replace this exitems with the clayout ones..
33230                 xitems = clayout.items;
33231                  
33232                 
33233                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
33234                     cfg.background = false;
33235                 }
33236                 var layout = new Roo.BorderLayout(el, clayout);
33237                 
33238                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
33239                 //console.log('adding nested layout panel '  + cfg.toSource());
33240                 this.add(region, ret);
33241                 nb = {}; /// find first...
33242                 break;
33243                 
33244             case 'GridPanel': 
33245             
33246                 // needs grid and region
33247                 
33248                 //var el = this.getRegion(region).el.createChild();
33249                 var el = this.el.createChild();
33250                 // create the grid first...
33251                 
33252                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
33253                 delete cfg.grid;
33254                 if (region == 'center' && this.active ) {
33255                     cfg.background = false;
33256                 }
33257                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
33258                 
33259                 this.add(region, ret);
33260                 if (cfg.background) {
33261                     ret.on('activate', function(gp) {
33262                         if (!gp.grid.rendered) {
33263                             gp.grid.render();
33264                         }
33265                     });
33266                 } else {
33267                     grid.render();
33268                 }
33269                 break;
33270            
33271            
33272            
33273                 
33274                 
33275                 
33276             default:
33277                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
33278                     
33279                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33280                     this.add(region, ret);
33281                 } else {
33282                 
33283                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
33284                     return null;
33285                 }
33286                 
33287              // GridPanel (grid, cfg)
33288             
33289         }
33290         this.beginUpdate();
33291         // add children..
33292         var region = '';
33293         var abn = {};
33294         Roo.each(xitems, function(i)  {
33295             region = nb && i.region ? i.region : false;
33296             
33297             var add = ret.addxtype(i);
33298            
33299             if (region) {
33300                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
33301                 if (!i.background) {
33302                     abn[region] = nb[region] ;
33303                 }
33304             }
33305             
33306         });
33307         this.endUpdate();
33308
33309         // make the last non-background panel active..
33310         //if (nb) { Roo.log(abn); }
33311         if (nb) {
33312             
33313             for(var r in abn) {
33314                 region = this.getRegion(r);
33315                 if (region) {
33316                     // tried using nb[r], but it does not work..
33317                      
33318                     region.showPanel(abn[r]);
33319                    
33320                 }
33321             }
33322         }
33323         return ret;
33324         
33325     }
33326 });
33327
33328 /**
33329  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
33330  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
33331  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
33332  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
33333  * <pre><code>
33334 // shorthand
33335 var CP = Roo.ContentPanel;
33336
33337 var layout = Roo.BorderLayout.create({
33338     north: {
33339         initialSize: 25,
33340         titlebar: false,
33341         panels: [new CP("north", "North")]
33342     },
33343     west: {
33344         split:true,
33345         initialSize: 200,
33346         minSize: 175,
33347         maxSize: 400,
33348         titlebar: true,
33349         collapsible: true,
33350         panels: [new CP("west", {title: "West"})]
33351     },
33352     east: {
33353         split:true,
33354         initialSize: 202,
33355         minSize: 175,
33356         maxSize: 400,
33357         titlebar: true,
33358         collapsible: true,
33359         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
33360     },
33361     south: {
33362         split:true,
33363         initialSize: 100,
33364         minSize: 100,
33365         maxSize: 200,
33366         titlebar: true,
33367         collapsible: true,
33368         panels: [new CP("south", {title: "South", closable: true})]
33369     },
33370     center: {
33371         titlebar: true,
33372         autoScroll:true,
33373         resizeTabs: true,
33374         minTabWidth: 50,
33375         preferredTabWidth: 150,
33376         panels: [
33377             new CP("center1", {title: "Close Me", closable: true}),
33378             new CP("center2", {title: "Center Panel", closable: false})
33379         ]
33380     }
33381 }, document.body);
33382
33383 layout.getRegion("center").showPanel("center1");
33384 </code></pre>
33385  * @param config
33386  * @param targetEl
33387  */
33388 Roo.BorderLayout.create = function(config, targetEl){
33389     var layout = new Roo.BorderLayout(targetEl || document.body, config);
33390     layout.beginUpdate();
33391     var regions = Roo.BorderLayout.RegionFactory.validRegions;
33392     for(var j = 0, jlen = regions.length; j < jlen; j++){
33393         var lr = regions[j];
33394         if(layout.regions[lr] && config[lr].panels){
33395             var r = layout.regions[lr];
33396             var ps = config[lr].panels;
33397             layout.addTypedPanels(r, ps);
33398         }
33399     }
33400     layout.endUpdate();
33401     return layout;
33402 };
33403
33404 // private
33405 Roo.BorderLayout.RegionFactory = {
33406     // private
33407     validRegions : ["north","south","east","west","center"],
33408
33409     // private
33410     create : function(target, mgr, config){
33411         target = target.toLowerCase();
33412         if(config.lightweight || config.basic){
33413             return new Roo.BasicLayoutRegion(mgr, config, target);
33414         }
33415         switch(target){
33416             case "north":
33417                 return new Roo.NorthLayoutRegion(mgr, config);
33418             case "south":
33419                 return new Roo.SouthLayoutRegion(mgr, config);
33420             case "east":
33421                 return new Roo.EastLayoutRegion(mgr, config);
33422             case "west":
33423                 return new Roo.WestLayoutRegion(mgr, config);
33424             case "center":
33425                 return new Roo.CenterLayoutRegion(mgr, config);
33426         }
33427         throw 'Layout region "'+target+'" not supported.';
33428     }
33429 };/*
33430  * Based on:
33431  * Ext JS Library 1.1.1
33432  * Copyright(c) 2006-2007, Ext JS, LLC.
33433  *
33434  * Originally Released Under LGPL - original licence link has changed is not relivant.
33435  *
33436  * Fork - LGPL
33437  * <script type="text/javascript">
33438  */
33439  
33440 /**
33441  * @class Roo.BasicLayoutRegion
33442  * @extends Roo.util.Observable
33443  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
33444  * and does not have a titlebar, tabs or any other features. All it does is size and position 
33445  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
33446  */
33447 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
33448     this.mgr = mgr;
33449     this.position  = pos;
33450     this.events = {
33451         /**
33452          * @scope Roo.BasicLayoutRegion
33453          */
33454         
33455         /**
33456          * @event beforeremove
33457          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
33458          * @param {Roo.LayoutRegion} this
33459          * @param {Roo.ContentPanel} panel The panel
33460          * @param {Object} e The cancel event object
33461          */
33462         "beforeremove" : true,
33463         /**
33464          * @event invalidated
33465          * Fires when the layout for this region is changed.
33466          * @param {Roo.LayoutRegion} this
33467          */
33468         "invalidated" : true,
33469         /**
33470          * @event visibilitychange
33471          * Fires when this region is shown or hidden 
33472          * @param {Roo.LayoutRegion} this
33473          * @param {Boolean} visibility true or false
33474          */
33475         "visibilitychange" : true,
33476         /**
33477          * @event paneladded
33478          * Fires when a panel is added. 
33479          * @param {Roo.LayoutRegion} this
33480          * @param {Roo.ContentPanel} panel The panel
33481          */
33482         "paneladded" : true,
33483         /**
33484          * @event panelremoved
33485          * Fires when a panel is removed. 
33486          * @param {Roo.LayoutRegion} this
33487          * @param {Roo.ContentPanel} panel The panel
33488          */
33489         "panelremoved" : true,
33490         /**
33491          * @event collapsed
33492          * Fires when this region is collapsed.
33493          * @param {Roo.LayoutRegion} this
33494          */
33495         "collapsed" : true,
33496         /**
33497          * @event expanded
33498          * Fires when this region is expanded.
33499          * @param {Roo.LayoutRegion} this
33500          */
33501         "expanded" : true,
33502         /**
33503          * @event slideshow
33504          * Fires when this region is slid into view.
33505          * @param {Roo.LayoutRegion} this
33506          */
33507         "slideshow" : true,
33508         /**
33509          * @event slidehide
33510          * Fires when this region slides out of view. 
33511          * @param {Roo.LayoutRegion} this
33512          */
33513         "slidehide" : true,
33514         /**
33515          * @event panelactivated
33516          * Fires when a panel is activated. 
33517          * @param {Roo.LayoutRegion} this
33518          * @param {Roo.ContentPanel} panel The activated panel
33519          */
33520         "panelactivated" : true,
33521         /**
33522          * @event resized
33523          * Fires when the user resizes this region. 
33524          * @param {Roo.LayoutRegion} this
33525          * @param {Number} newSize The new size (width for east/west, height for north/south)
33526          */
33527         "resized" : true
33528     };
33529     /** A collection of panels in this region. @type Roo.util.MixedCollection */
33530     this.panels = new Roo.util.MixedCollection();
33531     this.panels.getKey = this.getPanelId.createDelegate(this);
33532     this.box = null;
33533     this.activePanel = null;
33534     // ensure listeners are added...
33535     
33536     if (config.listeners || config.events) {
33537         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
33538             listeners : config.listeners || {},
33539             events : config.events || {}
33540         });
33541     }
33542     
33543     if(skipConfig !== true){
33544         this.applyConfig(config);
33545     }
33546 };
33547
33548 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
33549     getPanelId : function(p){
33550         return p.getId();
33551     },
33552     
33553     applyConfig : function(config){
33554         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33555         this.config = config;
33556         
33557     },
33558     
33559     /**
33560      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
33561      * the width, for horizontal (north, south) the height.
33562      * @param {Number} newSize The new width or height
33563      */
33564     resizeTo : function(newSize){
33565         var el = this.el ? this.el :
33566                  (this.activePanel ? this.activePanel.getEl() : null);
33567         if(el){
33568             switch(this.position){
33569                 case "east":
33570                 case "west":
33571                     el.setWidth(newSize);
33572                     this.fireEvent("resized", this, newSize);
33573                 break;
33574                 case "north":
33575                 case "south":
33576                     el.setHeight(newSize);
33577                     this.fireEvent("resized", this, newSize);
33578                 break;                
33579             }
33580         }
33581     },
33582     
33583     getBox : function(){
33584         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33585     },
33586     
33587     getMargins : function(){
33588         return this.margins;
33589     },
33590     
33591     updateBox : function(box){
33592         this.box = box;
33593         var el = this.activePanel.getEl();
33594         el.dom.style.left = box.x + "px";
33595         el.dom.style.top = box.y + "px";
33596         this.activePanel.setSize(box.width, box.height);
33597     },
33598     
33599     /**
33600      * Returns the container element for this region.
33601      * @return {Roo.Element}
33602      */
33603     getEl : function(){
33604         return this.activePanel;
33605     },
33606     
33607     /**
33608      * Returns true if this region is currently visible.
33609      * @return {Boolean}
33610      */
33611     isVisible : function(){
33612         return this.activePanel ? true : false;
33613     },
33614     
33615     setActivePanel : function(panel){
33616         panel = this.getPanel(panel);
33617         if(this.activePanel && this.activePanel != panel){
33618             this.activePanel.setActiveState(false);
33619             this.activePanel.getEl().setLeftTop(-10000,-10000);
33620         }
33621         this.activePanel = panel;
33622         panel.setActiveState(true);
33623         if(this.box){
33624             panel.setSize(this.box.width, this.box.height);
33625         }
33626         this.fireEvent("panelactivated", this, panel);
33627         this.fireEvent("invalidated");
33628     },
33629     
33630     /**
33631      * Show the specified panel.
33632      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33633      * @return {Roo.ContentPanel} The shown panel or null
33634      */
33635     showPanel : function(panel){
33636         if(panel = this.getPanel(panel)){
33637             this.setActivePanel(panel);
33638         }
33639         return panel;
33640     },
33641     
33642     /**
33643      * Get the active panel for this region.
33644      * @return {Roo.ContentPanel} The active panel or null
33645      */
33646     getActivePanel : function(){
33647         return this.activePanel;
33648     },
33649     
33650     /**
33651      * Add the passed ContentPanel(s)
33652      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33653      * @return {Roo.ContentPanel} The panel added (if only one was added)
33654      */
33655     add : function(panel){
33656         if(arguments.length > 1){
33657             for(var i = 0, len = arguments.length; i < len; i++) {
33658                 this.add(arguments[i]);
33659             }
33660             return null;
33661         }
33662         if(this.hasPanel(panel)){
33663             this.showPanel(panel);
33664             return panel;
33665         }
33666         var el = panel.getEl();
33667         if(el.dom.parentNode != this.mgr.el.dom){
33668             this.mgr.el.dom.appendChild(el.dom);
33669         }
33670         if(panel.setRegion){
33671             panel.setRegion(this);
33672         }
33673         this.panels.add(panel);
33674         el.setStyle("position", "absolute");
33675         if(!panel.background){
33676             this.setActivePanel(panel);
33677             if(this.config.initialSize && this.panels.getCount()==1){
33678                 this.resizeTo(this.config.initialSize);
33679             }
33680         }
33681         this.fireEvent("paneladded", this, panel);
33682         return panel;
33683     },
33684     
33685     /**
33686      * Returns true if the panel is in this region.
33687      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33688      * @return {Boolean}
33689      */
33690     hasPanel : function(panel){
33691         if(typeof panel == "object"){ // must be panel obj
33692             panel = panel.getId();
33693         }
33694         return this.getPanel(panel) ? true : false;
33695     },
33696     
33697     /**
33698      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33699      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33700      * @param {Boolean} preservePanel Overrides the config preservePanel option
33701      * @return {Roo.ContentPanel} The panel that was removed
33702      */
33703     remove : function(panel, preservePanel){
33704         panel = this.getPanel(panel);
33705         if(!panel){
33706             return null;
33707         }
33708         var e = {};
33709         this.fireEvent("beforeremove", this, panel, e);
33710         if(e.cancel === true){
33711             return null;
33712         }
33713         var panelId = panel.getId();
33714         this.panels.removeKey(panelId);
33715         return panel;
33716     },
33717     
33718     /**
33719      * Returns the panel specified or null if it's not in this region.
33720      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33721      * @return {Roo.ContentPanel}
33722      */
33723     getPanel : function(id){
33724         if(typeof id == "object"){ // must be panel obj
33725             return id;
33726         }
33727         return this.panels.get(id);
33728     },
33729     
33730     /**
33731      * Returns this regions position (north/south/east/west/center).
33732      * @return {String} 
33733      */
33734     getPosition: function(){
33735         return this.position;    
33736     }
33737 });/*
33738  * Based on:
33739  * Ext JS Library 1.1.1
33740  * Copyright(c) 2006-2007, Ext JS, LLC.
33741  *
33742  * Originally Released Under LGPL - original licence link has changed is not relivant.
33743  *
33744  * Fork - LGPL
33745  * <script type="text/javascript">
33746  */
33747  
33748 /**
33749  * @class Roo.LayoutRegion
33750  * @extends Roo.BasicLayoutRegion
33751  * This class represents a region in a layout manager.
33752  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
33753  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
33754  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
33755  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33756  * @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})
33757  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
33758  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
33759  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
33760  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
33761  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
33762  * @cfg {String}    title           The title for the region (overrides panel titles)
33763  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
33764  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33765  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
33766  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33767  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
33768  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33769  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
33770  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
33771  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
33772  * @cfg {Boolean}   showPin         True to show a pin button
33773  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
33774  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
33775  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
33776  * @cfg {Number}    width           For East/West panels
33777  * @cfg {Number}    height          For North/South panels
33778  * @cfg {Boolean}   split           To show the splitter
33779  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
33780  */
33781 Roo.LayoutRegion = function(mgr, config, pos){
33782     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
33783     var dh = Roo.DomHelper;
33784     /** This region's container element 
33785     * @type Roo.Element */
33786     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
33787     /** This region's title element 
33788     * @type Roo.Element */
33789
33790     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
33791         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
33792         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
33793     ]}, true);
33794     this.titleEl.enableDisplayMode();
33795     /** This region's title text element 
33796     * @type HTMLElement */
33797     this.titleTextEl = this.titleEl.dom.firstChild;
33798     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33799     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
33800     this.closeBtn.enableDisplayMode();
33801     this.closeBtn.on("click", this.closeClicked, this);
33802     this.closeBtn.hide();
33803
33804     this.createBody(config);
33805     this.visible = true;
33806     this.collapsed = false;
33807
33808     if(config.hideWhenEmpty){
33809         this.hide();
33810         this.on("paneladded", this.validateVisibility, this);
33811         this.on("panelremoved", this.validateVisibility, this);
33812     }
33813     this.applyConfig(config);
33814 };
33815
33816 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
33817
33818     createBody : function(){
33819         /** This region's body element 
33820         * @type Roo.Element */
33821         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
33822     },
33823
33824     applyConfig : function(c){
33825         if(c.collapsible && this.position != "center" && !this.collapsedEl){
33826             var dh = Roo.DomHelper;
33827             if(c.titlebar !== false){
33828                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
33829                 this.collapseBtn.on("click", this.collapse, this);
33830                 this.collapseBtn.enableDisplayMode();
33831
33832                 if(c.showPin === true || this.showPin){
33833                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
33834                     this.stickBtn.enableDisplayMode();
33835                     this.stickBtn.on("click", this.expand, this);
33836                     this.stickBtn.hide();
33837                 }
33838             }
33839             /** This region's collapsed element
33840             * @type Roo.Element */
33841             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33842                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33843             ]}, true);
33844             if(c.floatable !== false){
33845                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33846                this.collapsedEl.on("click", this.collapseClick, this);
33847             }
33848
33849             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33850                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33851                    id: "message", unselectable: "on", style:{"float":"left"}});
33852                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33853              }
33854             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33855             this.expandBtn.on("click", this.expand, this);
33856         }
33857         if(this.collapseBtn){
33858             this.collapseBtn.setVisible(c.collapsible == true);
33859         }
33860         this.cmargins = c.cmargins || this.cmargins ||
33861                          (this.position == "west" || this.position == "east" ?
33862                              {top: 0, left: 2, right:2, bottom: 0} :
33863                              {top: 2, left: 0, right:0, bottom: 2});
33864         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33865         this.bottomTabs = c.tabPosition != "top";
33866         this.autoScroll = c.autoScroll || false;
33867         if(this.autoScroll){
33868             this.bodyEl.setStyle("overflow", "auto");
33869         }else{
33870             this.bodyEl.setStyle("overflow", "hidden");
33871         }
33872         //if(c.titlebar !== false){
33873             if((!c.titlebar && !c.title) || c.titlebar === false){
33874                 this.titleEl.hide();
33875             }else{
33876                 this.titleEl.show();
33877                 if(c.title){
33878                     this.titleTextEl.innerHTML = c.title;
33879                 }
33880             }
33881         //}
33882         this.duration = c.duration || .30;
33883         this.slideDuration = c.slideDuration || .45;
33884         this.config = c;
33885         if(c.collapsed){
33886             this.collapse(true);
33887         }
33888         if(c.hidden){
33889             this.hide();
33890         }
33891     },
33892     /**
33893      * Returns true if this region is currently visible.
33894      * @return {Boolean}
33895      */
33896     isVisible : function(){
33897         return this.visible;
33898     },
33899
33900     /**
33901      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33902      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
33903      */
33904     setCollapsedTitle : function(title){
33905         title = title || "&#160;";
33906         if(this.collapsedTitleTextEl){
33907             this.collapsedTitleTextEl.innerHTML = title;
33908         }
33909     },
33910
33911     getBox : function(){
33912         var b;
33913         if(!this.collapsed){
33914             b = this.el.getBox(false, true);
33915         }else{
33916             b = this.collapsedEl.getBox(false, true);
33917         }
33918         return b;
33919     },
33920
33921     getMargins : function(){
33922         return this.collapsed ? this.cmargins : this.margins;
33923     },
33924
33925     highlight : function(){
33926         this.el.addClass("x-layout-panel-dragover");
33927     },
33928
33929     unhighlight : function(){
33930         this.el.removeClass("x-layout-panel-dragover");
33931     },
33932
33933     updateBox : function(box){
33934         this.box = box;
33935         if(!this.collapsed){
33936             this.el.dom.style.left = box.x + "px";
33937             this.el.dom.style.top = box.y + "px";
33938             this.updateBody(box.width, box.height);
33939         }else{
33940             this.collapsedEl.dom.style.left = box.x + "px";
33941             this.collapsedEl.dom.style.top = box.y + "px";
33942             this.collapsedEl.setSize(box.width, box.height);
33943         }
33944         if(this.tabs){
33945             this.tabs.autoSizeTabs();
33946         }
33947     },
33948
33949     updateBody : function(w, h){
33950         if(w !== null){
33951             this.el.setWidth(w);
33952             w -= this.el.getBorderWidth("rl");
33953             if(this.config.adjustments){
33954                 w += this.config.adjustments[0];
33955             }
33956         }
33957         if(h !== null){
33958             this.el.setHeight(h);
33959             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33960             h -= this.el.getBorderWidth("tb");
33961             if(this.config.adjustments){
33962                 h += this.config.adjustments[1];
33963             }
33964             this.bodyEl.setHeight(h);
33965             if(this.tabs){
33966                 h = this.tabs.syncHeight(h);
33967             }
33968         }
33969         if(this.panelSize){
33970             w = w !== null ? w : this.panelSize.width;
33971             h = h !== null ? h : this.panelSize.height;
33972         }
33973         if(this.activePanel){
33974             var el = this.activePanel.getEl();
33975             w = w !== null ? w : el.getWidth();
33976             h = h !== null ? h : el.getHeight();
33977             this.panelSize = {width: w, height: h};
33978             this.activePanel.setSize(w, h);
33979         }
33980         if(Roo.isIE && this.tabs){
33981             this.tabs.el.repaint();
33982         }
33983     },
33984
33985     /**
33986      * Returns the container element for this region.
33987      * @return {Roo.Element}
33988      */
33989     getEl : function(){
33990         return this.el;
33991     },
33992
33993     /**
33994      * Hides this region.
33995      */
33996     hide : function(){
33997         if(!this.collapsed){
33998             this.el.dom.style.left = "-2000px";
33999             this.el.hide();
34000         }else{
34001             this.collapsedEl.dom.style.left = "-2000px";
34002             this.collapsedEl.hide();
34003         }
34004         this.visible = false;
34005         this.fireEvent("visibilitychange", this, false);
34006     },
34007
34008     /**
34009      * Shows this region if it was previously hidden.
34010      */
34011     show : function(){
34012         if(!this.collapsed){
34013             this.el.show();
34014         }else{
34015             this.collapsedEl.show();
34016         }
34017         this.visible = true;
34018         this.fireEvent("visibilitychange", this, true);
34019     },
34020
34021     closeClicked : function(){
34022         if(this.activePanel){
34023             this.remove(this.activePanel);
34024         }
34025     },
34026
34027     collapseClick : function(e){
34028         if(this.isSlid){
34029            e.stopPropagation();
34030            this.slideIn();
34031         }else{
34032            e.stopPropagation();
34033            this.slideOut();
34034         }
34035     },
34036
34037     /**
34038      * Collapses this region.
34039      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
34040      */
34041     collapse : function(skipAnim){
34042         if(this.collapsed) return;
34043         this.collapsed = true;
34044         if(this.split){
34045             this.split.el.hide();
34046         }
34047         if(this.config.animate && skipAnim !== true){
34048             this.fireEvent("invalidated", this);
34049             this.animateCollapse();
34050         }else{
34051             this.el.setLocation(-20000,-20000);
34052             this.el.hide();
34053             this.collapsedEl.show();
34054             this.fireEvent("collapsed", this);
34055             this.fireEvent("invalidated", this);
34056         }
34057     },
34058
34059     animateCollapse : function(){
34060         // overridden
34061     },
34062
34063     /**
34064      * Expands this region if it was previously collapsed.
34065      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
34066      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
34067      */
34068     expand : function(e, skipAnim){
34069         if(e) e.stopPropagation();
34070         if(!this.collapsed || this.el.hasActiveFx()) return;
34071         if(this.isSlid){
34072             this.afterSlideIn();
34073             skipAnim = true;
34074         }
34075         this.collapsed = false;
34076         if(this.config.animate && skipAnim !== true){
34077             this.animateExpand();
34078         }else{
34079             this.el.show();
34080             if(this.split){
34081                 this.split.el.show();
34082             }
34083             this.collapsedEl.setLocation(-2000,-2000);
34084             this.collapsedEl.hide();
34085             this.fireEvent("invalidated", this);
34086             this.fireEvent("expanded", this);
34087         }
34088     },
34089
34090     animateExpand : function(){
34091         // overridden
34092     },
34093
34094     initTabs : function()
34095     {
34096         this.bodyEl.setStyle("overflow", "hidden");
34097         var ts = new Roo.TabPanel(
34098                 this.bodyEl.dom,
34099                 {
34100                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
34101                     disableTooltips: this.config.disableTabTips,
34102                     toolbar : this.config.toolbar
34103                 }
34104         );
34105         if(this.config.hideTabs){
34106             ts.stripWrap.setDisplayed(false);
34107         }
34108         this.tabs = ts;
34109         ts.resizeTabs = this.config.resizeTabs === true;
34110         ts.minTabWidth = this.config.minTabWidth || 40;
34111         ts.maxTabWidth = this.config.maxTabWidth || 250;
34112         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
34113         ts.monitorResize = false;
34114         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34115         ts.bodyEl.addClass('x-layout-tabs-body');
34116         this.panels.each(this.initPanelAsTab, this);
34117     },
34118
34119     initPanelAsTab : function(panel){
34120         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
34121                     this.config.closeOnTab && panel.isClosable());
34122         if(panel.tabTip !== undefined){
34123             ti.setTooltip(panel.tabTip);
34124         }
34125         ti.on("activate", function(){
34126               this.setActivePanel(panel);
34127         }, this);
34128         if(this.config.closeOnTab){
34129             ti.on("beforeclose", function(t, e){
34130                 e.cancel = true;
34131                 this.remove(panel);
34132             }, this);
34133         }
34134         return ti;
34135     },
34136
34137     updatePanelTitle : function(panel, title){
34138         if(this.activePanel == panel){
34139             this.updateTitle(title);
34140         }
34141         if(this.tabs){
34142             var ti = this.tabs.getTab(panel.getEl().id);
34143             ti.setText(title);
34144             if(panel.tabTip !== undefined){
34145                 ti.setTooltip(panel.tabTip);
34146             }
34147         }
34148     },
34149
34150     updateTitle : function(title){
34151         if(this.titleTextEl && !this.config.title){
34152             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
34153         }
34154     },
34155
34156     setActivePanel : function(panel){
34157         panel = this.getPanel(panel);
34158         if(this.activePanel && this.activePanel != panel){
34159             this.activePanel.setActiveState(false);
34160         }
34161         this.activePanel = panel;
34162         panel.setActiveState(true);
34163         if(this.panelSize){
34164             panel.setSize(this.panelSize.width, this.panelSize.height);
34165         }
34166         if(this.closeBtn){
34167             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
34168         }
34169         this.updateTitle(panel.getTitle());
34170         if(this.tabs){
34171             this.fireEvent("invalidated", this);
34172         }
34173         this.fireEvent("panelactivated", this, panel);
34174     },
34175
34176     /**
34177      * Shows the specified panel.
34178      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
34179      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
34180      */
34181     showPanel : function(panel)
34182     {
34183         panel = this.getPanel(panel);
34184         if(panel){
34185             if(this.tabs){
34186                 var tab = this.tabs.getTab(panel.getEl().id);
34187                 if(tab.isHidden()){
34188                     this.tabs.unhideTab(tab.id);
34189                 }
34190                 tab.activate();
34191             }else{
34192                 this.setActivePanel(panel);
34193             }
34194         }
34195         return panel;
34196     },
34197
34198     /**
34199      * Get the active panel for this region.
34200      * @return {Roo.ContentPanel} The active panel or null
34201      */
34202     getActivePanel : function(){
34203         return this.activePanel;
34204     },
34205
34206     validateVisibility : function(){
34207         if(this.panels.getCount() < 1){
34208             this.updateTitle("&#160;");
34209             this.closeBtn.hide();
34210             this.hide();
34211         }else{
34212             if(!this.isVisible()){
34213                 this.show();
34214             }
34215         }
34216     },
34217
34218     /**
34219      * Adds the passed ContentPanel(s) to this region.
34220      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34221      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
34222      */
34223     add : function(panel){
34224         if(arguments.length > 1){
34225             for(var i = 0, len = arguments.length; i < len; i++) {
34226                 this.add(arguments[i]);
34227             }
34228             return null;
34229         }
34230         if(this.hasPanel(panel)){
34231             this.showPanel(panel);
34232             return panel;
34233         }
34234         panel.setRegion(this);
34235         this.panels.add(panel);
34236         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
34237             this.bodyEl.dom.appendChild(panel.getEl().dom);
34238             if(panel.background !== true){
34239                 this.setActivePanel(panel);
34240             }
34241             this.fireEvent("paneladded", this, panel);
34242             return panel;
34243         }
34244         if(!this.tabs){
34245             this.initTabs();
34246         }else{
34247             this.initPanelAsTab(panel);
34248         }
34249         if(panel.background !== true){
34250             this.tabs.activate(panel.getEl().id);
34251         }
34252         this.fireEvent("paneladded", this, panel);
34253         return panel;
34254     },
34255
34256     /**
34257      * Hides the tab for the specified panel.
34258      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34259      */
34260     hidePanel : function(panel){
34261         if(this.tabs && (panel = this.getPanel(panel))){
34262             this.tabs.hideTab(panel.getEl().id);
34263         }
34264     },
34265
34266     /**
34267      * Unhides the tab for a previously hidden panel.
34268      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34269      */
34270     unhidePanel : function(panel){
34271         if(this.tabs && (panel = this.getPanel(panel))){
34272             this.tabs.unhideTab(panel.getEl().id);
34273         }
34274     },
34275
34276     clearPanels : function(){
34277         while(this.panels.getCount() > 0){
34278              this.remove(this.panels.first());
34279         }
34280     },
34281
34282     /**
34283      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34284      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34285      * @param {Boolean} preservePanel Overrides the config preservePanel option
34286      * @return {Roo.ContentPanel} The panel that was removed
34287      */
34288     remove : function(panel, preservePanel){
34289         panel = this.getPanel(panel);
34290         if(!panel){
34291             return null;
34292         }
34293         var e = {};
34294         this.fireEvent("beforeremove", this, panel, e);
34295         if(e.cancel === true){
34296             return null;
34297         }
34298         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
34299         var panelId = panel.getId();
34300         this.panels.removeKey(panelId);
34301         if(preservePanel){
34302             document.body.appendChild(panel.getEl().dom);
34303         }
34304         if(this.tabs){
34305             this.tabs.removeTab(panel.getEl().id);
34306         }else if (!preservePanel){
34307             this.bodyEl.dom.removeChild(panel.getEl().dom);
34308         }
34309         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
34310             var p = this.panels.first();
34311             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
34312             tempEl.appendChild(p.getEl().dom);
34313             this.bodyEl.update("");
34314             this.bodyEl.dom.appendChild(p.getEl().dom);
34315             tempEl = null;
34316             this.updateTitle(p.getTitle());
34317             this.tabs = null;
34318             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34319             this.setActivePanel(p);
34320         }
34321         panel.setRegion(null);
34322         if(this.activePanel == panel){
34323             this.activePanel = null;
34324         }
34325         if(this.config.autoDestroy !== false && preservePanel !== true){
34326             try{panel.destroy();}catch(e){}
34327         }
34328         this.fireEvent("panelremoved", this, panel);
34329         return panel;
34330     },
34331
34332     /**
34333      * Returns the TabPanel component used by this region
34334      * @return {Roo.TabPanel}
34335      */
34336     getTabs : function(){
34337         return this.tabs;
34338     },
34339
34340     createTool : function(parentEl, className){
34341         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
34342             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
34343         btn.addClassOnOver("x-layout-tools-button-over");
34344         return btn;
34345     }
34346 });/*
34347  * Based on:
34348  * Ext JS Library 1.1.1
34349  * Copyright(c) 2006-2007, Ext JS, LLC.
34350  *
34351  * Originally Released Under LGPL - original licence link has changed is not relivant.
34352  *
34353  * Fork - LGPL
34354  * <script type="text/javascript">
34355  */
34356  
34357
34358
34359 /**
34360  * @class Roo.SplitLayoutRegion
34361  * @extends Roo.LayoutRegion
34362  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
34363  */
34364 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
34365     this.cursor = cursor;
34366     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
34367 };
34368
34369 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
34370     splitTip : "Drag to resize.",
34371     collapsibleSplitTip : "Drag to resize. Double click to hide.",
34372     useSplitTips : false,
34373
34374     applyConfig : function(config){
34375         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
34376         if(config.split){
34377             if(!this.split){
34378                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
34379                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
34380                 /** The SplitBar for this region 
34381                 * @type Roo.SplitBar */
34382                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
34383                 this.split.on("moved", this.onSplitMove, this);
34384                 this.split.useShim = config.useShim === true;
34385                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
34386                 if(this.useSplitTips){
34387                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
34388                 }
34389                 if(config.collapsible){
34390                     this.split.el.on("dblclick", this.collapse,  this);
34391                 }
34392             }
34393             if(typeof config.minSize != "undefined"){
34394                 this.split.minSize = config.minSize;
34395             }
34396             if(typeof config.maxSize != "undefined"){
34397                 this.split.maxSize = config.maxSize;
34398             }
34399             if(config.hideWhenEmpty || config.hidden || config.collapsed){
34400                 this.hideSplitter();
34401             }
34402         }
34403     },
34404
34405     getHMaxSize : function(){
34406          var cmax = this.config.maxSize || 10000;
34407          var center = this.mgr.getRegion("center");
34408          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
34409     },
34410
34411     getVMaxSize : function(){
34412          var cmax = this.config.maxSize || 10000;
34413          var center = this.mgr.getRegion("center");
34414          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
34415     },
34416
34417     onSplitMove : function(split, newSize){
34418         this.fireEvent("resized", this, newSize);
34419     },
34420     
34421     /** 
34422      * Returns the {@link Roo.SplitBar} for this region.
34423      * @return {Roo.SplitBar}
34424      */
34425     getSplitBar : function(){
34426         return this.split;
34427     },
34428     
34429     hide : function(){
34430         this.hideSplitter();
34431         Roo.SplitLayoutRegion.superclass.hide.call(this);
34432     },
34433
34434     hideSplitter : function(){
34435         if(this.split){
34436             this.split.el.setLocation(-2000,-2000);
34437             this.split.el.hide();
34438         }
34439     },
34440
34441     show : function(){
34442         if(this.split){
34443             this.split.el.show();
34444         }
34445         Roo.SplitLayoutRegion.superclass.show.call(this);
34446     },
34447     
34448     beforeSlide: function(){
34449         if(Roo.isGecko){// firefox overflow auto bug workaround
34450             this.bodyEl.clip();
34451             if(this.tabs) this.tabs.bodyEl.clip();
34452             if(this.activePanel){
34453                 this.activePanel.getEl().clip();
34454                 
34455                 if(this.activePanel.beforeSlide){
34456                     this.activePanel.beforeSlide();
34457                 }
34458             }
34459         }
34460     },
34461     
34462     afterSlide : function(){
34463         if(Roo.isGecko){// firefox overflow auto bug workaround
34464             this.bodyEl.unclip();
34465             if(this.tabs) this.tabs.bodyEl.unclip();
34466             if(this.activePanel){
34467                 this.activePanel.getEl().unclip();
34468                 if(this.activePanel.afterSlide){
34469                     this.activePanel.afterSlide();
34470                 }
34471             }
34472         }
34473     },
34474
34475     initAutoHide : function(){
34476         if(this.autoHide !== false){
34477             if(!this.autoHideHd){
34478                 var st = new Roo.util.DelayedTask(this.slideIn, this);
34479                 this.autoHideHd = {
34480                     "mouseout": function(e){
34481                         if(!e.within(this.el, true)){
34482                             st.delay(500);
34483                         }
34484                     },
34485                     "mouseover" : function(e){
34486                         st.cancel();
34487                     },
34488                     scope : this
34489                 };
34490             }
34491             this.el.on(this.autoHideHd);
34492         }
34493     },
34494
34495     clearAutoHide : function(){
34496         if(this.autoHide !== false){
34497             this.el.un("mouseout", this.autoHideHd.mouseout);
34498             this.el.un("mouseover", this.autoHideHd.mouseover);
34499         }
34500     },
34501
34502     clearMonitor : function(){
34503         Roo.get(document).un("click", this.slideInIf, this);
34504     },
34505
34506     // these names are backwards but not changed for compat
34507     slideOut : function(){
34508         if(this.isSlid || this.el.hasActiveFx()){
34509             return;
34510         }
34511         this.isSlid = true;
34512         if(this.collapseBtn){
34513             this.collapseBtn.hide();
34514         }
34515         this.closeBtnState = this.closeBtn.getStyle('display');
34516         this.closeBtn.hide();
34517         if(this.stickBtn){
34518             this.stickBtn.show();
34519         }
34520         this.el.show();
34521         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34522         this.beforeSlide();
34523         this.el.setStyle("z-index", 10001);
34524         this.el.slideIn(this.getSlideAnchor(), {
34525             callback: function(){
34526                 this.afterSlide();
34527                 this.initAutoHide();
34528                 Roo.get(document).on("click", this.slideInIf, this);
34529                 this.fireEvent("slideshow", this);
34530             },
34531             scope: this,
34532             block: true
34533         });
34534     },
34535
34536     afterSlideIn : function(){
34537         this.clearAutoHide();
34538         this.isSlid = false;
34539         this.clearMonitor();
34540         this.el.setStyle("z-index", "");
34541         if(this.collapseBtn){
34542             this.collapseBtn.show();
34543         }
34544         this.closeBtn.setStyle('display', this.closeBtnState);
34545         if(this.stickBtn){
34546             this.stickBtn.hide();
34547         }
34548         this.fireEvent("slidehide", this);
34549     },
34550
34551     slideIn : function(cb){
34552         if(!this.isSlid || this.el.hasActiveFx()){
34553             Roo.callback(cb);
34554             return;
34555         }
34556         this.isSlid = false;
34557         this.beforeSlide();
34558         this.el.slideOut(this.getSlideAnchor(), {
34559             callback: function(){
34560                 this.el.setLeftTop(-10000, -10000);
34561                 this.afterSlide();
34562                 this.afterSlideIn();
34563                 Roo.callback(cb);
34564             },
34565             scope: this,
34566             block: true
34567         });
34568     },
34569     
34570     slideInIf : function(e){
34571         if(!e.within(this.el)){
34572             this.slideIn();
34573         }
34574     },
34575
34576     animateCollapse : function(){
34577         this.beforeSlide();
34578         this.el.setStyle("z-index", 20000);
34579         var anchor = this.getSlideAnchor();
34580         this.el.slideOut(anchor, {
34581             callback : function(){
34582                 this.el.setStyle("z-index", "");
34583                 this.collapsedEl.slideIn(anchor, {duration:.3});
34584                 this.afterSlide();
34585                 this.el.setLocation(-10000,-10000);
34586                 this.el.hide();
34587                 this.fireEvent("collapsed", this);
34588             },
34589             scope: this,
34590             block: true
34591         });
34592     },
34593
34594     animateExpand : function(){
34595         this.beforeSlide();
34596         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34597         this.el.setStyle("z-index", 20000);
34598         this.collapsedEl.hide({
34599             duration:.1
34600         });
34601         this.el.slideIn(this.getSlideAnchor(), {
34602             callback : function(){
34603                 this.el.setStyle("z-index", "");
34604                 this.afterSlide();
34605                 if(this.split){
34606                     this.split.el.show();
34607                 }
34608                 this.fireEvent("invalidated", this);
34609                 this.fireEvent("expanded", this);
34610             },
34611             scope: this,
34612             block: true
34613         });
34614     },
34615
34616     anchors : {
34617         "west" : "left",
34618         "east" : "right",
34619         "north" : "top",
34620         "south" : "bottom"
34621     },
34622
34623     sanchors : {
34624         "west" : "l",
34625         "east" : "r",
34626         "north" : "t",
34627         "south" : "b"
34628     },
34629
34630     canchors : {
34631         "west" : "tl-tr",
34632         "east" : "tr-tl",
34633         "north" : "tl-bl",
34634         "south" : "bl-tl"
34635     },
34636
34637     getAnchor : function(){
34638         return this.anchors[this.position];
34639     },
34640
34641     getCollapseAnchor : function(){
34642         return this.canchors[this.position];
34643     },
34644
34645     getSlideAnchor : function(){
34646         return this.sanchors[this.position];
34647     },
34648
34649     getAlignAdj : function(){
34650         var cm = this.cmargins;
34651         switch(this.position){
34652             case "west":
34653                 return [0, 0];
34654             break;
34655             case "east":
34656                 return [0, 0];
34657             break;
34658             case "north":
34659                 return [0, 0];
34660             break;
34661             case "south":
34662                 return [0, 0];
34663             break;
34664         }
34665     },
34666
34667     getExpandAdj : function(){
34668         var c = this.collapsedEl, cm = this.cmargins;
34669         switch(this.position){
34670             case "west":
34671                 return [-(cm.right+c.getWidth()+cm.left), 0];
34672             break;
34673             case "east":
34674                 return [cm.right+c.getWidth()+cm.left, 0];
34675             break;
34676             case "north":
34677                 return [0, -(cm.top+cm.bottom+c.getHeight())];
34678             break;
34679             case "south":
34680                 return [0, cm.top+cm.bottom+c.getHeight()];
34681             break;
34682         }
34683     }
34684 });/*
34685  * Based on:
34686  * Ext JS Library 1.1.1
34687  * Copyright(c) 2006-2007, Ext JS, LLC.
34688  *
34689  * Originally Released Under LGPL - original licence link has changed is not relivant.
34690  *
34691  * Fork - LGPL
34692  * <script type="text/javascript">
34693  */
34694 /*
34695  * These classes are private internal classes
34696  */
34697 Roo.CenterLayoutRegion = function(mgr, config){
34698     Roo.LayoutRegion.call(this, mgr, config, "center");
34699     this.visible = true;
34700     this.minWidth = config.minWidth || 20;
34701     this.minHeight = config.minHeight || 20;
34702 };
34703
34704 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
34705     hide : function(){
34706         // center panel can't be hidden
34707     },
34708     
34709     show : function(){
34710         // center panel can't be hidden
34711     },
34712     
34713     getMinWidth: function(){
34714         return this.minWidth;
34715     },
34716     
34717     getMinHeight: function(){
34718         return this.minHeight;
34719     }
34720 });
34721
34722
34723 Roo.NorthLayoutRegion = function(mgr, config){
34724     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
34725     if(this.split){
34726         this.split.placement = Roo.SplitBar.TOP;
34727         this.split.orientation = Roo.SplitBar.VERTICAL;
34728         this.split.el.addClass("x-layout-split-v");
34729     }
34730     var size = config.initialSize || config.height;
34731     if(typeof size != "undefined"){
34732         this.el.setHeight(size);
34733     }
34734 };
34735 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
34736     orientation: Roo.SplitBar.VERTICAL,
34737     getBox : function(){
34738         if(this.collapsed){
34739             return this.collapsedEl.getBox();
34740         }
34741         var box = this.el.getBox();
34742         if(this.split){
34743             box.height += this.split.el.getHeight();
34744         }
34745         return box;
34746     },
34747     
34748     updateBox : function(box){
34749         if(this.split && !this.collapsed){
34750             box.height -= this.split.el.getHeight();
34751             this.split.el.setLeft(box.x);
34752             this.split.el.setTop(box.y+box.height);
34753             this.split.el.setWidth(box.width);
34754         }
34755         if(this.collapsed){
34756             this.updateBody(box.width, null);
34757         }
34758         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34759     }
34760 });
34761
34762 Roo.SouthLayoutRegion = function(mgr, config){
34763     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
34764     if(this.split){
34765         this.split.placement = Roo.SplitBar.BOTTOM;
34766         this.split.orientation = Roo.SplitBar.VERTICAL;
34767         this.split.el.addClass("x-layout-split-v");
34768     }
34769     var size = config.initialSize || config.height;
34770     if(typeof size != "undefined"){
34771         this.el.setHeight(size);
34772     }
34773 };
34774 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
34775     orientation: Roo.SplitBar.VERTICAL,
34776     getBox : function(){
34777         if(this.collapsed){
34778             return this.collapsedEl.getBox();
34779         }
34780         var box = this.el.getBox();
34781         if(this.split){
34782             var sh = this.split.el.getHeight();
34783             box.height += sh;
34784             box.y -= sh;
34785         }
34786         return box;
34787     },
34788     
34789     updateBox : function(box){
34790         if(this.split && !this.collapsed){
34791             var sh = this.split.el.getHeight();
34792             box.height -= sh;
34793             box.y += sh;
34794             this.split.el.setLeft(box.x);
34795             this.split.el.setTop(box.y-sh);
34796             this.split.el.setWidth(box.width);
34797         }
34798         if(this.collapsed){
34799             this.updateBody(box.width, null);
34800         }
34801         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34802     }
34803 });
34804
34805 Roo.EastLayoutRegion = function(mgr, config){
34806     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
34807     if(this.split){
34808         this.split.placement = Roo.SplitBar.RIGHT;
34809         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34810         this.split.el.addClass("x-layout-split-h");
34811     }
34812     var size = config.initialSize || config.width;
34813     if(typeof size != "undefined"){
34814         this.el.setWidth(size);
34815     }
34816 };
34817 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
34818     orientation: Roo.SplitBar.HORIZONTAL,
34819     getBox : function(){
34820         if(this.collapsed){
34821             return this.collapsedEl.getBox();
34822         }
34823         var box = this.el.getBox();
34824         if(this.split){
34825             var sw = this.split.el.getWidth();
34826             box.width += sw;
34827             box.x -= sw;
34828         }
34829         return box;
34830     },
34831
34832     updateBox : function(box){
34833         if(this.split && !this.collapsed){
34834             var sw = this.split.el.getWidth();
34835             box.width -= sw;
34836             this.split.el.setLeft(box.x);
34837             this.split.el.setTop(box.y);
34838             this.split.el.setHeight(box.height);
34839             box.x += sw;
34840         }
34841         if(this.collapsed){
34842             this.updateBody(null, box.height);
34843         }
34844         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34845     }
34846 });
34847
34848 Roo.WestLayoutRegion = function(mgr, config){
34849     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
34850     if(this.split){
34851         this.split.placement = Roo.SplitBar.LEFT;
34852         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34853         this.split.el.addClass("x-layout-split-h");
34854     }
34855     var size = config.initialSize || config.width;
34856     if(typeof size != "undefined"){
34857         this.el.setWidth(size);
34858     }
34859 };
34860 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
34861     orientation: Roo.SplitBar.HORIZONTAL,
34862     getBox : function(){
34863         if(this.collapsed){
34864             return this.collapsedEl.getBox();
34865         }
34866         var box = this.el.getBox();
34867         if(this.split){
34868             box.width += this.split.el.getWidth();
34869         }
34870         return box;
34871     },
34872     
34873     updateBox : function(box){
34874         if(this.split && !this.collapsed){
34875             var sw = this.split.el.getWidth();
34876             box.width -= sw;
34877             this.split.el.setLeft(box.x+box.width);
34878             this.split.el.setTop(box.y);
34879             this.split.el.setHeight(box.height);
34880         }
34881         if(this.collapsed){
34882             this.updateBody(null, box.height);
34883         }
34884         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34885     }
34886 });
34887 /*
34888  * Based on:
34889  * Ext JS Library 1.1.1
34890  * Copyright(c) 2006-2007, Ext JS, LLC.
34891  *
34892  * Originally Released Under LGPL - original licence link has changed is not relivant.
34893  *
34894  * Fork - LGPL
34895  * <script type="text/javascript">
34896  */
34897  
34898  
34899 /*
34900  * Private internal class for reading and applying state
34901  */
34902 Roo.LayoutStateManager = function(layout){
34903      // default empty state
34904      this.state = {
34905         north: {},
34906         south: {},
34907         east: {},
34908         west: {}       
34909     };
34910 };
34911
34912 Roo.LayoutStateManager.prototype = {
34913     init : function(layout, provider){
34914         this.provider = provider;
34915         var state = provider.get(layout.id+"-layout-state");
34916         if(state){
34917             var wasUpdating = layout.isUpdating();
34918             if(!wasUpdating){
34919                 layout.beginUpdate();
34920             }
34921             for(var key in state){
34922                 if(typeof state[key] != "function"){
34923                     var rstate = state[key];
34924                     var r = layout.getRegion(key);
34925                     if(r && rstate){
34926                         if(rstate.size){
34927                             r.resizeTo(rstate.size);
34928                         }
34929                         if(rstate.collapsed == true){
34930                             r.collapse(true);
34931                         }else{
34932                             r.expand(null, true);
34933                         }
34934                     }
34935                 }
34936             }
34937             if(!wasUpdating){
34938                 layout.endUpdate();
34939             }
34940             this.state = state; 
34941         }
34942         this.layout = layout;
34943         layout.on("regionresized", this.onRegionResized, this);
34944         layout.on("regioncollapsed", this.onRegionCollapsed, this);
34945         layout.on("regionexpanded", this.onRegionExpanded, this);
34946     },
34947     
34948     storeState : function(){
34949         this.provider.set(this.layout.id+"-layout-state", this.state);
34950     },
34951     
34952     onRegionResized : function(region, newSize){
34953         this.state[region.getPosition()].size = newSize;
34954         this.storeState();
34955     },
34956     
34957     onRegionCollapsed : function(region){
34958         this.state[region.getPosition()].collapsed = true;
34959         this.storeState();
34960     },
34961     
34962     onRegionExpanded : function(region){
34963         this.state[region.getPosition()].collapsed = false;
34964         this.storeState();
34965     }
34966 };/*
34967  * Based on:
34968  * Ext JS Library 1.1.1
34969  * Copyright(c) 2006-2007, Ext JS, LLC.
34970  *
34971  * Originally Released Under LGPL - original licence link has changed is not relivant.
34972  *
34973  * Fork - LGPL
34974  * <script type="text/javascript">
34975  */
34976 /**
34977  * @class Roo.ContentPanel
34978  * @extends Roo.util.Observable
34979  * A basic ContentPanel element.
34980  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
34981  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
34982  * @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
34983  * @cfg {Boolean}   closable      True if the panel can be closed/removed
34984  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
34985  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34986  * @cfg {Toolbar}   toolbar       A toolbar for this panel
34987  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
34988  * @cfg {String} title          The title for this panel
34989  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34990  * @cfg {String} url            Calls {@link #setUrl} with this value
34991  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34992  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
34993  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
34994  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
34995
34996  * @constructor
34997  * Create a new ContentPanel.
34998  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34999  * @param {String/Object} config A string to set only the title or a config object
35000  * @param {String} content (optional) Set the HTML content for this panel
35001  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
35002  */
35003 Roo.ContentPanel = function(el, config, content){
35004     
35005      
35006     /*
35007     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
35008         config = el;
35009         el = Roo.id();
35010     }
35011     if (config && config.parentLayout) { 
35012         el = config.parentLayout.el.createChild(); 
35013     }
35014     */
35015     if(el.autoCreate){ // xtype is available if this is called from factory
35016         config = el;
35017         el = Roo.id();
35018     }
35019     this.el = Roo.get(el);
35020     if(!this.el && config && config.autoCreate){
35021         if(typeof config.autoCreate == "object"){
35022             if(!config.autoCreate.id){
35023                 config.autoCreate.id = config.id||el;
35024             }
35025             this.el = Roo.DomHelper.append(document.body,
35026                         config.autoCreate, true);
35027         }else{
35028             this.el = Roo.DomHelper.append(document.body,
35029                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
35030         }
35031     }
35032     this.closable = false;
35033     this.loaded = false;
35034     this.active = false;
35035     if(typeof config == "string"){
35036         this.title = config;
35037     }else{
35038         Roo.apply(this, config);
35039     }
35040     
35041     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
35042         this.wrapEl = this.el.wrap();
35043         this.toolbar.container = this.el.insertSibling(false, 'before');
35044         this.toolbar = new Roo.Toolbar(this.toolbar);
35045     }
35046     
35047     // xtype created footer. - not sure if will work as we normally have to render first..
35048     if (this.footer && !this.footer.el && this.footer.xtype) {
35049         if (!this.wrapEl) {
35050             this.wrapEl = this.el.wrap();
35051         }
35052     
35053         this.footer.container = this.wrapEl.createChild();
35054          
35055         this.footer = Roo.factory(this.footer, Roo);
35056         
35057     }
35058     
35059     if(this.resizeEl){
35060         this.resizeEl = Roo.get(this.resizeEl, true);
35061     }else{
35062         this.resizeEl = this.el;
35063     }
35064     // handle view.xtype
35065     
35066  
35067     
35068     
35069     this.addEvents({
35070         /**
35071          * @event activate
35072          * Fires when this panel is activated. 
35073          * @param {Roo.ContentPanel} this
35074          */
35075         "activate" : true,
35076         /**
35077          * @event deactivate
35078          * Fires when this panel is activated. 
35079          * @param {Roo.ContentPanel} this
35080          */
35081         "deactivate" : true,
35082
35083         /**
35084          * @event resize
35085          * Fires when this panel is resized if fitToFrame is true.
35086          * @param {Roo.ContentPanel} this
35087          * @param {Number} width The width after any component adjustments
35088          * @param {Number} height The height after any component adjustments
35089          */
35090         "resize" : true,
35091         
35092          /**
35093          * @event render
35094          * Fires when this tab is created
35095          * @param {Roo.ContentPanel} this
35096          */
35097         "render" : true
35098         
35099         
35100         
35101     });
35102     
35103
35104     
35105     
35106     if(this.autoScroll){
35107         this.resizeEl.setStyle("overflow", "auto");
35108     } else {
35109         // fix randome scrolling
35110         this.el.on('scroll', function() {
35111             Roo.log('fix random scolling');
35112             this.scrollTo('top',0); 
35113         });
35114     }
35115     content = content || this.content;
35116     if(content){
35117         this.setContent(content);
35118     }
35119     if(config && config.url){
35120         this.setUrl(this.url, this.params, this.loadOnce);
35121     }
35122     
35123     
35124     
35125     Roo.ContentPanel.superclass.constructor.call(this);
35126     
35127     if (this.view && typeof(this.view.xtype) != 'undefined') {
35128         this.view.el = this.el.appendChild(document.createElement("div"));
35129         this.view = Roo.factory(this.view); 
35130         this.view.render  &&  this.view.render(false, '');  
35131     }
35132     
35133     
35134     this.fireEvent('render', this);
35135 };
35136
35137 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
35138     tabTip:'',
35139     setRegion : function(region){
35140         this.region = region;
35141         if(region){
35142            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
35143         }else{
35144            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
35145         } 
35146     },
35147     
35148     /**
35149      * Returns the toolbar for this Panel if one was configured. 
35150      * @return {Roo.Toolbar} 
35151      */
35152     getToolbar : function(){
35153         return this.toolbar;
35154     },
35155     
35156     setActiveState : function(active){
35157         this.active = active;
35158         if(!active){
35159             this.fireEvent("deactivate", this);
35160         }else{
35161             this.fireEvent("activate", this);
35162         }
35163     },
35164     /**
35165      * Updates this panel's element
35166      * @param {String} content The new content
35167      * @param {Boolean} loadScripts (optional) true to look for and process scripts
35168     */
35169     setContent : function(content, loadScripts){
35170         this.el.update(content, loadScripts);
35171     },
35172
35173     ignoreResize : function(w, h){
35174         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
35175             return true;
35176         }else{
35177             this.lastSize = {width: w, height: h};
35178             return false;
35179         }
35180     },
35181     /**
35182      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
35183      * @return {Roo.UpdateManager} The UpdateManager
35184      */
35185     getUpdateManager : function(){
35186         return this.el.getUpdateManager();
35187     },
35188      /**
35189      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
35190      * @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:
35191 <pre><code>
35192 panel.load({
35193     url: "your-url.php",
35194     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
35195     callback: yourFunction,
35196     scope: yourObject, //(optional scope)
35197     discardUrl: false,
35198     nocache: false,
35199     text: "Loading...",
35200     timeout: 30,
35201     scripts: false
35202 });
35203 </code></pre>
35204      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
35205      * 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.
35206      * @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}
35207      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
35208      * @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.
35209      * @return {Roo.ContentPanel} this
35210      */
35211     load : function(){
35212         var um = this.el.getUpdateManager();
35213         um.update.apply(um, arguments);
35214         return this;
35215     },
35216
35217
35218     /**
35219      * 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.
35220      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
35221      * @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)
35222      * @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)
35223      * @return {Roo.UpdateManager} The UpdateManager
35224      */
35225     setUrl : function(url, params, loadOnce){
35226         if(this.refreshDelegate){
35227             this.removeListener("activate", this.refreshDelegate);
35228         }
35229         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35230         this.on("activate", this.refreshDelegate);
35231         return this.el.getUpdateManager();
35232     },
35233     
35234     _handleRefresh : function(url, params, loadOnce){
35235         if(!loadOnce || !this.loaded){
35236             var updater = this.el.getUpdateManager();
35237             updater.update(url, params, this._setLoaded.createDelegate(this));
35238         }
35239     },
35240     
35241     _setLoaded : function(){
35242         this.loaded = true;
35243     }, 
35244     
35245     /**
35246      * Returns this panel's id
35247      * @return {String} 
35248      */
35249     getId : function(){
35250         return this.el.id;
35251     },
35252     
35253     /** 
35254      * Returns this panel's element - used by regiosn to add.
35255      * @return {Roo.Element} 
35256      */
35257     getEl : function(){
35258         return this.wrapEl || this.el;
35259     },
35260     
35261     adjustForComponents : function(width, height)
35262     {
35263         //Roo.log('adjustForComponents ');
35264         if(this.resizeEl != this.el){
35265             width -= this.el.getFrameWidth('lr');
35266             height -= this.el.getFrameWidth('tb');
35267         }
35268         if(this.toolbar){
35269             var te = this.toolbar.getEl();
35270             height -= te.getHeight();
35271             te.setWidth(width);
35272         }
35273         if(this.footer){
35274             var te = this.footer.getEl();
35275             Roo.log("footer:" + te.getHeight());
35276             
35277             height -= te.getHeight();
35278             te.setWidth(width);
35279         }
35280         
35281         
35282         if(this.adjustments){
35283             width += this.adjustments[0];
35284             height += this.adjustments[1];
35285         }
35286         return {"width": width, "height": height};
35287     },
35288     
35289     setSize : function(width, height){
35290         if(this.fitToFrame && !this.ignoreResize(width, height)){
35291             if(this.fitContainer && this.resizeEl != this.el){
35292                 this.el.setSize(width, height);
35293             }
35294             var size = this.adjustForComponents(width, height);
35295             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
35296             this.fireEvent('resize', this, size.width, size.height);
35297         }
35298     },
35299     
35300     /**
35301      * Returns this panel's title
35302      * @return {String} 
35303      */
35304     getTitle : function(){
35305         return this.title;
35306     },
35307     
35308     /**
35309      * Set this panel's title
35310      * @param {String} title
35311      */
35312     setTitle : function(title){
35313         this.title = title;
35314         if(this.region){
35315             this.region.updatePanelTitle(this, title);
35316         }
35317     },
35318     
35319     /**
35320      * Returns true is this panel was configured to be closable
35321      * @return {Boolean} 
35322      */
35323     isClosable : function(){
35324         return this.closable;
35325     },
35326     
35327     beforeSlide : function(){
35328         this.el.clip();
35329         this.resizeEl.clip();
35330     },
35331     
35332     afterSlide : function(){
35333         this.el.unclip();
35334         this.resizeEl.unclip();
35335     },
35336     
35337     /**
35338      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
35339      *   Will fail silently if the {@link #setUrl} method has not been called.
35340      *   This does not activate the panel, just updates its content.
35341      */
35342     refresh : function(){
35343         if(this.refreshDelegate){
35344            this.loaded = false;
35345            this.refreshDelegate();
35346         }
35347     },
35348     
35349     /**
35350      * Destroys this panel
35351      */
35352     destroy : function(){
35353         this.el.removeAllListeners();
35354         var tempEl = document.createElement("span");
35355         tempEl.appendChild(this.el.dom);
35356         tempEl.innerHTML = "";
35357         this.el.remove();
35358         this.el = null;
35359     },
35360     
35361     /**
35362      * form - if the content panel contains a form - this is a reference to it.
35363      * @type {Roo.form.Form}
35364      */
35365     form : false,
35366     /**
35367      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
35368      *    This contains a reference to it.
35369      * @type {Roo.View}
35370      */
35371     view : false,
35372     
35373       /**
35374      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
35375      * <pre><code>
35376
35377 layout.addxtype({
35378        xtype : 'Form',
35379        items: [ .... ]
35380    }
35381 );
35382
35383 </code></pre>
35384      * @param {Object} cfg Xtype definition of item to add.
35385      */
35386     
35387     addxtype : function(cfg) {
35388         // add form..
35389         if (cfg.xtype.match(/^Form$/)) {
35390             
35391             var el;
35392             //if (this.footer) {
35393             //    el = this.footer.container.insertSibling(false, 'before');
35394             //} else {
35395                 el = this.el.createChild();
35396             //}
35397
35398             this.form = new  Roo.form.Form(cfg);
35399             
35400             
35401             if ( this.form.allItems.length) this.form.render(el.dom);
35402             return this.form;
35403         }
35404         // should only have one of theses..
35405         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
35406             // views.. should not be just added - used named prop 'view''
35407             
35408             cfg.el = this.el.appendChild(document.createElement("div"));
35409             // factory?
35410             
35411             var ret = new Roo.factory(cfg);
35412              
35413              ret.render && ret.render(false, ''); // render blank..
35414             this.view = ret;
35415             return ret;
35416         }
35417         return false;
35418     }
35419 });
35420
35421 /**
35422  * @class Roo.GridPanel
35423  * @extends Roo.ContentPanel
35424  * @constructor
35425  * Create a new GridPanel.
35426  * @param {Roo.grid.Grid} grid The grid for this panel
35427  * @param {String/Object} config A string to set only the panel's title, or a config object
35428  */
35429 Roo.GridPanel = function(grid, config){
35430     
35431   
35432     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
35433         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
35434         
35435     this.wrapper.dom.appendChild(grid.getGridEl().dom);
35436     
35437     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
35438     
35439     if(this.toolbar){
35440         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
35441     }
35442     // xtype created footer. - not sure if will work as we normally have to render first..
35443     if (this.footer && !this.footer.el && this.footer.xtype) {
35444         
35445         this.footer.container = this.grid.getView().getFooterPanel(true);
35446         this.footer.dataSource = this.grid.dataSource;
35447         this.footer = Roo.factory(this.footer, Roo);
35448         
35449     }
35450     
35451     grid.monitorWindowResize = false; // turn off autosizing
35452     grid.autoHeight = false;
35453     grid.autoWidth = false;
35454     this.grid = grid;
35455     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
35456 };
35457
35458 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
35459     getId : function(){
35460         return this.grid.id;
35461     },
35462     
35463     /**
35464      * Returns the grid for this panel
35465      * @return {Roo.grid.Grid} 
35466      */
35467     getGrid : function(){
35468         return this.grid;    
35469     },
35470     
35471     setSize : function(width, height){
35472         if(!this.ignoreResize(width, height)){
35473             var grid = this.grid;
35474             var size = this.adjustForComponents(width, height);
35475             grid.getGridEl().setSize(size.width, size.height);
35476             grid.autoSize();
35477         }
35478     },
35479     
35480     beforeSlide : function(){
35481         this.grid.getView().scroller.clip();
35482     },
35483     
35484     afterSlide : function(){
35485         this.grid.getView().scroller.unclip();
35486     },
35487     
35488     destroy : function(){
35489         this.grid.destroy();
35490         delete this.grid;
35491         Roo.GridPanel.superclass.destroy.call(this); 
35492     }
35493 });
35494
35495
35496 /**
35497  * @class Roo.NestedLayoutPanel
35498  * @extends Roo.ContentPanel
35499  * @constructor
35500  * Create a new NestedLayoutPanel.
35501  * 
35502  * 
35503  * @param {Roo.BorderLayout} layout The layout for this panel
35504  * @param {String/Object} config A string to set only the title or a config object
35505  */
35506 Roo.NestedLayoutPanel = function(layout, config)
35507 {
35508     // construct with only one argument..
35509     /* FIXME - implement nicer consturctors
35510     if (layout.layout) {
35511         config = layout;
35512         layout = config.layout;
35513         delete config.layout;
35514     }
35515     if (layout.xtype && !layout.getEl) {
35516         // then layout needs constructing..
35517         layout = Roo.factory(layout, Roo);
35518     }
35519     */
35520     
35521     
35522     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
35523     
35524     layout.monitorWindowResize = false; // turn off autosizing
35525     this.layout = layout;
35526     this.layout.getEl().addClass("x-layout-nested-layout");
35527     
35528     
35529     
35530     
35531 };
35532
35533 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
35534
35535     setSize : function(width, height){
35536         if(!this.ignoreResize(width, height)){
35537             var size = this.adjustForComponents(width, height);
35538             var el = this.layout.getEl();
35539             el.setSize(size.width, size.height);
35540             var touch = el.dom.offsetWidth;
35541             this.layout.layout();
35542             // ie requires a double layout on the first pass
35543             if(Roo.isIE && !this.initialized){
35544                 this.initialized = true;
35545                 this.layout.layout();
35546             }
35547         }
35548     },
35549     
35550     // activate all subpanels if not currently active..
35551     
35552     setActiveState : function(active){
35553         this.active = active;
35554         if(!active){
35555             this.fireEvent("deactivate", this);
35556             return;
35557         }
35558         
35559         this.fireEvent("activate", this);
35560         // not sure if this should happen before or after..
35561         if (!this.layout) {
35562             return; // should not happen..
35563         }
35564         var reg = false;
35565         for (var r in this.layout.regions) {
35566             reg = this.layout.getRegion(r);
35567             if (reg.getActivePanel()) {
35568                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
35569                 reg.setActivePanel(reg.getActivePanel());
35570                 continue;
35571             }
35572             if (!reg.panels.length) {
35573                 continue;
35574             }
35575             reg.showPanel(reg.getPanel(0));
35576         }
35577         
35578         
35579         
35580         
35581     },
35582     
35583     /**
35584      * Returns the nested BorderLayout for this panel
35585      * @return {Roo.BorderLayout} 
35586      */
35587     getLayout : function(){
35588         return this.layout;
35589     },
35590     
35591      /**
35592      * Adds a xtype elements to the layout of the nested panel
35593      * <pre><code>
35594
35595 panel.addxtype({
35596        xtype : 'ContentPanel',
35597        region: 'west',
35598        items: [ .... ]
35599    }
35600 );
35601
35602 panel.addxtype({
35603         xtype : 'NestedLayoutPanel',
35604         region: 'west',
35605         layout: {
35606            center: { },
35607            west: { }   
35608         },
35609         items : [ ... list of content panels or nested layout panels.. ]
35610    }
35611 );
35612 </code></pre>
35613      * @param {Object} cfg Xtype definition of item to add.
35614      */
35615     addxtype : function(cfg) {
35616         return this.layout.addxtype(cfg);
35617     
35618     }
35619 });
35620
35621 Roo.ScrollPanel = function(el, config, content){
35622     config = config || {};
35623     config.fitToFrame = true;
35624     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
35625     
35626     this.el.dom.style.overflow = "hidden";
35627     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
35628     this.el.removeClass("x-layout-inactive-content");
35629     this.el.on("mousewheel", this.onWheel, this);
35630
35631     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
35632     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
35633     up.unselectable(); down.unselectable();
35634     up.on("click", this.scrollUp, this);
35635     down.on("click", this.scrollDown, this);
35636     up.addClassOnOver("x-scroller-btn-over");
35637     down.addClassOnOver("x-scroller-btn-over");
35638     up.addClassOnClick("x-scroller-btn-click");
35639     down.addClassOnClick("x-scroller-btn-click");
35640     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
35641
35642     this.resizeEl = this.el;
35643     this.el = wrap; this.up = up; this.down = down;
35644 };
35645
35646 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
35647     increment : 100,
35648     wheelIncrement : 5,
35649     scrollUp : function(){
35650         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
35651     },
35652
35653     scrollDown : function(){
35654         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
35655     },
35656
35657     afterScroll : function(){
35658         var el = this.resizeEl;
35659         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
35660         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35661         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35662     },
35663
35664     setSize : function(){
35665         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
35666         this.afterScroll();
35667     },
35668
35669     onWheel : function(e){
35670         var d = e.getWheelDelta();
35671         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
35672         this.afterScroll();
35673         e.stopEvent();
35674     },
35675
35676     setContent : function(content, loadScripts){
35677         this.resizeEl.update(content, loadScripts);
35678     }
35679
35680 });
35681
35682
35683
35684
35685
35686
35687
35688
35689
35690 /**
35691  * @class Roo.TreePanel
35692  * @extends Roo.ContentPanel
35693  * @constructor
35694  * Create a new TreePanel. - defaults to fit/scoll contents.
35695  * @param {String/Object} config A string to set only the panel's title, or a config object
35696  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
35697  */
35698 Roo.TreePanel = function(config){
35699     var el = config.el;
35700     var tree = config.tree;
35701     delete config.tree; 
35702     delete config.el; // hopefull!
35703     
35704     // wrapper for IE7 strict & safari scroll issue
35705     
35706     var treeEl = el.createChild();
35707     config.resizeEl = treeEl;
35708     
35709     
35710     
35711     Roo.TreePanel.superclass.constructor.call(this, el, config);
35712  
35713  
35714     this.tree = new Roo.tree.TreePanel(treeEl , tree);
35715     //console.log(tree);
35716     this.on('activate', function()
35717     {
35718         if (this.tree.rendered) {
35719             return;
35720         }
35721         //console.log('render tree');
35722         this.tree.render();
35723     });
35724     // this should not be needed.. - it's actually the 'el' that resizes?
35725     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
35726     
35727     //this.on('resize',  function (cp, w, h) {
35728     //        this.tree.innerCt.setWidth(w);
35729     //        this.tree.innerCt.setHeight(h);
35730     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
35731     //});
35732
35733         
35734     
35735 };
35736
35737 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
35738     fitToFrame : true,
35739     autoScroll : true
35740 });
35741
35742
35743
35744
35745
35746
35747
35748
35749
35750
35751
35752 /*
35753  * Based on:
35754  * Ext JS Library 1.1.1
35755  * Copyright(c) 2006-2007, Ext JS, LLC.
35756  *
35757  * Originally Released Under LGPL - original licence link has changed is not relivant.
35758  *
35759  * Fork - LGPL
35760  * <script type="text/javascript">
35761  */
35762  
35763
35764 /**
35765  * @class Roo.ReaderLayout
35766  * @extends Roo.BorderLayout
35767  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
35768  * center region containing two nested regions (a top one for a list view and one for item preview below),
35769  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
35770  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
35771  * expedites the setup of the overall layout and regions for this common application style.
35772  * Example:
35773  <pre><code>
35774 var reader = new Roo.ReaderLayout();
35775 var CP = Roo.ContentPanel;  // shortcut for adding
35776
35777 reader.beginUpdate();
35778 reader.add("north", new CP("north", "North"));
35779 reader.add("west", new CP("west", {title: "West"}));
35780 reader.add("east", new CP("east", {title: "East"}));
35781
35782 reader.regions.listView.add(new CP("listView", "List"));
35783 reader.regions.preview.add(new CP("preview", "Preview"));
35784 reader.endUpdate();
35785 </code></pre>
35786 * @constructor
35787 * Create a new ReaderLayout
35788 * @param {Object} config Configuration options
35789 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
35790 * document.body if omitted)
35791 */
35792 Roo.ReaderLayout = function(config, renderTo){
35793     var c = config || {size:{}};
35794     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
35795         north: c.north !== false ? Roo.apply({
35796             split:false,
35797             initialSize: 32,
35798             titlebar: false
35799         }, c.north) : false,
35800         west: c.west !== false ? Roo.apply({
35801             split:true,
35802             initialSize: 200,
35803             minSize: 175,
35804             maxSize: 400,
35805             titlebar: true,
35806             collapsible: true,
35807             animate: true,
35808             margins:{left:5,right:0,bottom:5,top:5},
35809             cmargins:{left:5,right:5,bottom:5,top:5}
35810         }, c.west) : false,
35811         east: c.east !== false ? Roo.apply({
35812             split:true,
35813             initialSize: 200,
35814             minSize: 175,
35815             maxSize: 400,
35816             titlebar: true,
35817             collapsible: true,
35818             animate: true,
35819             margins:{left:0,right:5,bottom:5,top:5},
35820             cmargins:{left:5,right:5,bottom:5,top:5}
35821         }, c.east) : false,
35822         center: Roo.apply({
35823             tabPosition: 'top',
35824             autoScroll:false,
35825             closeOnTab: true,
35826             titlebar:false,
35827             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
35828         }, c.center)
35829     });
35830
35831     this.el.addClass('x-reader');
35832
35833     this.beginUpdate();
35834
35835     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
35836         south: c.preview !== false ? Roo.apply({
35837             split:true,
35838             initialSize: 200,
35839             minSize: 100,
35840             autoScroll:true,
35841             collapsible:true,
35842             titlebar: true,
35843             cmargins:{top:5,left:0, right:0, bottom:0}
35844         }, c.preview) : false,
35845         center: Roo.apply({
35846             autoScroll:false,
35847             titlebar:false,
35848             minHeight:200
35849         }, c.listView)
35850     });
35851     this.add('center', new Roo.NestedLayoutPanel(inner,
35852             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
35853
35854     this.endUpdate();
35855
35856     this.regions.preview = inner.getRegion('south');
35857     this.regions.listView = inner.getRegion('center');
35858 };
35859
35860 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
35861  * Based on:
35862  * Ext JS Library 1.1.1
35863  * Copyright(c) 2006-2007, Ext JS, LLC.
35864  *
35865  * Originally Released Under LGPL - original licence link has changed is not relivant.
35866  *
35867  * Fork - LGPL
35868  * <script type="text/javascript">
35869  */
35870  
35871 /**
35872  * @class Roo.grid.Grid
35873  * @extends Roo.util.Observable
35874  * This class represents the primary interface of a component based grid control.
35875  * <br><br>Usage:<pre><code>
35876  var grid = new Roo.grid.Grid("my-container-id", {
35877      ds: myDataStore,
35878      cm: myColModel,
35879      selModel: mySelectionModel,
35880      autoSizeColumns: true,
35881      monitorWindowResize: false,
35882      trackMouseOver: true
35883  });
35884  // set any options
35885  grid.render();
35886  * </code></pre>
35887  * <b>Common Problems:</b><br/>
35888  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
35889  * element will correct this<br/>
35890  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
35891  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
35892  * are unpredictable.<br/>
35893  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
35894  * grid to calculate dimensions/offsets.<br/>
35895   * @constructor
35896  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
35897  * The container MUST have some type of size defined for the grid to fill. The container will be
35898  * automatically set to position relative if it isn't already.
35899  * @param {Object} config A config object that sets properties on this grid.
35900  */
35901 Roo.grid.Grid = function(container, config){
35902         // initialize the container
35903         this.container = Roo.get(container);
35904         this.container.update("");
35905         this.container.setStyle("overflow", "hidden");
35906     this.container.addClass('x-grid-container');
35907
35908     this.id = this.container.id;
35909
35910     Roo.apply(this, config);
35911     // check and correct shorthanded configs
35912     if(this.ds){
35913         this.dataSource = this.ds;
35914         delete this.ds;
35915     }
35916     if(this.cm){
35917         this.colModel = this.cm;
35918         delete this.cm;
35919     }
35920     if(this.sm){
35921         this.selModel = this.sm;
35922         delete this.sm;
35923     }
35924
35925     if (this.selModel) {
35926         this.selModel = Roo.factory(this.selModel, Roo.grid);
35927         this.sm = this.selModel;
35928         this.sm.xmodule = this.xmodule || false;
35929     }
35930     if (typeof(this.colModel.config) == 'undefined') {
35931         this.colModel = new Roo.grid.ColumnModel(this.colModel);
35932         this.cm = this.colModel;
35933         this.cm.xmodule = this.xmodule || false;
35934     }
35935     if (this.dataSource) {
35936         this.dataSource= Roo.factory(this.dataSource, Roo.data);
35937         this.ds = this.dataSource;
35938         this.ds.xmodule = this.xmodule || false;
35939          
35940     }
35941     
35942     
35943     
35944     if(this.width){
35945         this.container.setWidth(this.width);
35946     }
35947
35948     if(this.height){
35949         this.container.setHeight(this.height);
35950     }
35951     /** @private */
35952         this.addEvents({
35953         // raw events
35954         /**
35955          * @event click
35956          * The raw click event for the entire grid.
35957          * @param {Roo.EventObject} e
35958          */
35959         "click" : true,
35960         /**
35961          * @event dblclick
35962          * The raw dblclick event for the entire grid.
35963          * @param {Roo.EventObject} e
35964          */
35965         "dblclick" : true,
35966         /**
35967          * @event contextmenu
35968          * The raw contextmenu event for the entire grid.
35969          * @param {Roo.EventObject} e
35970          */
35971         "contextmenu" : true,
35972         /**
35973          * @event mousedown
35974          * The raw mousedown event for the entire grid.
35975          * @param {Roo.EventObject} e
35976          */
35977         "mousedown" : true,
35978         /**
35979          * @event mouseup
35980          * The raw mouseup event for the entire grid.
35981          * @param {Roo.EventObject} e
35982          */
35983         "mouseup" : true,
35984         /**
35985          * @event mouseover
35986          * The raw mouseover event for the entire grid.
35987          * @param {Roo.EventObject} e
35988          */
35989         "mouseover" : true,
35990         /**
35991          * @event mouseout
35992          * The raw mouseout event for the entire grid.
35993          * @param {Roo.EventObject} e
35994          */
35995         "mouseout" : true,
35996         /**
35997          * @event keypress
35998          * The raw keypress event for the entire grid.
35999          * @param {Roo.EventObject} e
36000          */
36001         "keypress" : true,
36002         /**
36003          * @event keydown
36004          * The raw keydown event for the entire grid.
36005          * @param {Roo.EventObject} e
36006          */
36007         "keydown" : true,
36008
36009         // custom events
36010
36011         /**
36012          * @event cellclick
36013          * Fires when a cell is clicked
36014          * @param {Grid} this
36015          * @param {Number} rowIndex
36016          * @param {Number} columnIndex
36017          * @param {Roo.EventObject} e
36018          */
36019         "cellclick" : true,
36020         /**
36021          * @event celldblclick
36022          * Fires when a cell is double clicked
36023          * @param {Grid} this
36024          * @param {Number} rowIndex
36025          * @param {Number} columnIndex
36026          * @param {Roo.EventObject} e
36027          */
36028         "celldblclick" : true,
36029         /**
36030          * @event rowclick
36031          * Fires when a row is clicked
36032          * @param {Grid} this
36033          * @param {Number} rowIndex
36034          * @param {Roo.EventObject} e
36035          */
36036         "rowclick" : true,
36037         /**
36038          * @event rowdblclick
36039          * Fires when a row is double clicked
36040          * @param {Grid} this
36041          * @param {Number} rowIndex
36042          * @param {Roo.EventObject} e
36043          */
36044         "rowdblclick" : true,
36045         /**
36046          * @event headerclick
36047          * Fires when a header is clicked
36048          * @param {Grid} this
36049          * @param {Number} columnIndex
36050          * @param {Roo.EventObject} e
36051          */
36052         "headerclick" : true,
36053         /**
36054          * @event headerdblclick
36055          * Fires when a header cell is double clicked
36056          * @param {Grid} this
36057          * @param {Number} columnIndex
36058          * @param {Roo.EventObject} e
36059          */
36060         "headerdblclick" : true,
36061         /**
36062          * @event rowcontextmenu
36063          * Fires when a row is right clicked
36064          * @param {Grid} this
36065          * @param {Number} rowIndex
36066          * @param {Roo.EventObject} e
36067          */
36068         "rowcontextmenu" : true,
36069         /**
36070          * @event cellcontextmenu
36071          * Fires when a cell is right clicked
36072          * @param {Grid} this
36073          * @param {Number} rowIndex
36074          * @param {Number} cellIndex
36075          * @param {Roo.EventObject} e
36076          */
36077          "cellcontextmenu" : true,
36078         /**
36079          * @event headercontextmenu
36080          * Fires when a header is right clicked
36081          * @param {Grid} this
36082          * @param {Number} columnIndex
36083          * @param {Roo.EventObject} e
36084          */
36085         "headercontextmenu" : true,
36086         /**
36087          * @event bodyscroll
36088          * Fires when the body element is scrolled
36089          * @param {Number} scrollLeft
36090          * @param {Number} scrollTop
36091          */
36092         "bodyscroll" : true,
36093         /**
36094          * @event columnresize
36095          * Fires when the user resizes a column
36096          * @param {Number} columnIndex
36097          * @param {Number} newSize
36098          */
36099         "columnresize" : true,
36100         /**
36101          * @event columnmove
36102          * Fires when the user moves a column
36103          * @param {Number} oldIndex
36104          * @param {Number} newIndex
36105          */
36106         "columnmove" : true,
36107         /**
36108          * @event startdrag
36109          * Fires when row(s) start being dragged
36110          * @param {Grid} this
36111          * @param {Roo.GridDD} dd The drag drop object
36112          * @param {event} e The raw browser event
36113          */
36114         "startdrag" : true,
36115         /**
36116          * @event enddrag
36117          * Fires when a drag operation is complete
36118          * @param {Grid} this
36119          * @param {Roo.GridDD} dd The drag drop object
36120          * @param {event} e The raw browser event
36121          */
36122         "enddrag" : true,
36123         /**
36124          * @event dragdrop
36125          * Fires when dragged row(s) are dropped on a valid DD target
36126          * @param {Grid} this
36127          * @param {Roo.GridDD} dd The drag drop object
36128          * @param {String} targetId The target drag drop object
36129          * @param {event} e The raw browser event
36130          */
36131         "dragdrop" : true,
36132         /**
36133          * @event dragover
36134          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
36135          * @param {Grid} this
36136          * @param {Roo.GridDD} dd The drag drop object
36137          * @param {String} targetId The target drag drop object
36138          * @param {event} e The raw browser event
36139          */
36140         "dragover" : true,
36141         /**
36142          * @event dragenter
36143          *  Fires when the dragged row(s) first cross another DD target while being dragged
36144          * @param {Grid} this
36145          * @param {Roo.GridDD} dd The drag drop object
36146          * @param {String} targetId The target drag drop object
36147          * @param {event} e The raw browser event
36148          */
36149         "dragenter" : true,
36150         /**
36151          * @event dragout
36152          * Fires when the dragged row(s) leave another DD target while being dragged
36153          * @param {Grid} this
36154          * @param {Roo.GridDD} dd The drag drop object
36155          * @param {String} targetId The target drag drop object
36156          * @param {event} e The raw browser event
36157          */
36158         "dragout" : true,
36159         /**
36160          * @event rowclass
36161          * Fires when a row is rendered, so you can change add a style to it.
36162          * @param {GridView} gridview   The grid view
36163          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
36164          */
36165         'rowclass' : true,
36166
36167         /**
36168          * @event render
36169          * Fires when the grid is rendered
36170          * @param {Grid} grid
36171          */
36172         'render' : true
36173     });
36174
36175     Roo.grid.Grid.superclass.constructor.call(this);
36176 };
36177 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
36178     
36179     /**
36180      * @cfg {String} ddGroup - drag drop group.
36181      */
36182
36183     /**
36184      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
36185      */
36186     minColumnWidth : 25,
36187
36188     /**
36189      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
36190      * <b>on initial render.</b> It is more efficient to explicitly size the columns
36191      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
36192      */
36193     autoSizeColumns : false,
36194
36195     /**
36196      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
36197      */
36198     autoSizeHeaders : true,
36199
36200     /**
36201      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
36202      */
36203     monitorWindowResize : true,
36204
36205     /**
36206      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
36207      * rows measured to get a columns size. Default is 0 (all rows).
36208      */
36209     maxRowsToMeasure : 0,
36210
36211     /**
36212      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
36213      */
36214     trackMouseOver : true,
36215
36216     /**
36217     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
36218     */
36219     
36220     /**
36221     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
36222     */
36223     enableDragDrop : false,
36224     
36225     /**
36226     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
36227     */
36228     enableColumnMove : true,
36229     
36230     /**
36231     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
36232     */
36233     enableColumnHide : true,
36234     
36235     /**
36236     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
36237     */
36238     enableRowHeightSync : false,
36239     
36240     /**
36241     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
36242     */
36243     stripeRows : true,
36244     
36245     /**
36246     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
36247     */
36248     autoHeight : false,
36249
36250     /**
36251      * @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.
36252      */
36253     autoExpandColumn : false,
36254
36255     /**
36256     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
36257     * Default is 50.
36258     */
36259     autoExpandMin : 50,
36260
36261     /**
36262     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
36263     */
36264     autoExpandMax : 1000,
36265
36266     /**
36267     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
36268     */
36269     view : null,
36270
36271     /**
36272     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
36273     */
36274     loadMask : false,
36275     /**
36276     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
36277     */
36278     dropTarget: false,
36279     
36280    
36281     
36282     // private
36283     rendered : false,
36284
36285     /**
36286     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
36287     * of a fixed width. Default is false.
36288     */
36289     /**
36290     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
36291     */
36292     /**
36293      * Called once after all setup has been completed and the grid is ready to be rendered.
36294      * @return {Roo.grid.Grid} this
36295      */
36296     render : function()
36297     {
36298         var c = this.container;
36299         // try to detect autoHeight/width mode
36300         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
36301             this.autoHeight = true;
36302         }
36303         var view = this.getView();
36304         view.init(this);
36305
36306         c.on("click", this.onClick, this);
36307         c.on("dblclick", this.onDblClick, this);
36308         c.on("contextmenu", this.onContextMenu, this);
36309         c.on("keydown", this.onKeyDown, this);
36310         if (Roo.isTouch) {
36311             c.on("touchstart", this.onTouchStart, this);
36312         }
36313
36314         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
36315
36316         this.getSelectionModel().init(this);
36317
36318         view.render();
36319
36320         if(this.loadMask){
36321             this.loadMask = new Roo.LoadMask(this.container,
36322                     Roo.apply({store:this.dataSource}, this.loadMask));
36323         }
36324         
36325         
36326         if (this.toolbar && this.toolbar.xtype) {
36327             this.toolbar.container = this.getView().getHeaderPanel(true);
36328             this.toolbar = new Roo.Toolbar(this.toolbar);
36329         }
36330         if (this.footer && this.footer.xtype) {
36331             this.footer.dataSource = this.getDataSource();
36332             this.footer.container = this.getView().getFooterPanel(true);
36333             this.footer = Roo.factory(this.footer, Roo);
36334         }
36335         if (this.dropTarget && this.dropTarget.xtype) {
36336             delete this.dropTarget.xtype;
36337             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
36338         }
36339         
36340         
36341         this.rendered = true;
36342         this.fireEvent('render', this);
36343         return this;
36344     },
36345
36346         /**
36347          * Reconfigures the grid to use a different Store and Column Model.
36348          * The View will be bound to the new objects and refreshed.
36349          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
36350          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
36351          */
36352     reconfigure : function(dataSource, colModel){
36353         if(this.loadMask){
36354             this.loadMask.destroy();
36355             this.loadMask = new Roo.LoadMask(this.container,
36356                     Roo.apply({store:dataSource}, this.loadMask));
36357         }
36358         this.view.bind(dataSource, colModel);
36359         this.dataSource = dataSource;
36360         this.colModel = colModel;
36361         this.view.refresh(true);
36362     },
36363
36364     // private
36365     onKeyDown : function(e){
36366         this.fireEvent("keydown", e);
36367     },
36368
36369     /**
36370      * Destroy this grid.
36371      * @param {Boolean} removeEl True to remove the element
36372      */
36373     destroy : function(removeEl, keepListeners){
36374         if(this.loadMask){
36375             this.loadMask.destroy();
36376         }
36377         var c = this.container;
36378         c.removeAllListeners();
36379         this.view.destroy();
36380         this.colModel.purgeListeners();
36381         if(!keepListeners){
36382             this.purgeListeners();
36383         }
36384         c.update("");
36385         if(removeEl === true){
36386             c.remove();
36387         }
36388     },
36389
36390     // private
36391     processEvent : function(name, e){
36392         // does this fire select???
36393         //Roo.log('grid:processEvent '  + name);
36394         
36395         if (name != 'touchstart' ) {
36396             this.fireEvent(name, e);    
36397         }
36398         
36399         var t = e.getTarget();
36400         var v = this.view;
36401         var header = v.findHeaderIndex(t);
36402         if(header !== false){
36403             var ename = name == 'touchstart' ? 'click' : name;
36404              
36405             this.fireEvent("header" + ename, this, header, e);
36406         }else{
36407             var row = v.findRowIndex(t);
36408             var cell = v.findCellIndex(t);
36409             if (name == 'touchstart') {
36410                 // first touch is always a click.
36411                 // hopefull this happens after selection is updated.?
36412                 name = false;
36413                 
36414                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
36415                     var cs = this.selModel.getSelectedCell();
36416                     if (row == cs[0] && cell == cs[1]){
36417                         name = 'dblclick';
36418                     }
36419                 }
36420                 if (typeof(this.selModel.getSelections) != 'undefined') {
36421                     var cs = this.selModel.getSelections();
36422                     var ds = this.dataSource;
36423                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
36424                         name = 'dblclick';
36425                     }
36426                 }
36427                 if (!name) {
36428                     return;
36429                 }
36430             }
36431             
36432             
36433             if(row !== false){
36434                 this.fireEvent("row" + name, this, row, e);
36435                 if(cell !== false){
36436                     this.fireEvent("cell" + name, this, row, cell, e);
36437                 }
36438             }
36439         }
36440     },
36441
36442     // private
36443     onClick : function(e){
36444         this.processEvent("click", e);
36445     },
36446    // private
36447     onTouchStart : function(e){
36448         this.processEvent("touchstart", e);
36449     },
36450
36451     // private
36452     onContextMenu : function(e, t){
36453         this.processEvent("contextmenu", e);
36454     },
36455
36456     // private
36457     onDblClick : function(e){
36458         this.processEvent("dblclick", e);
36459     },
36460
36461     // private
36462     walkCells : function(row, col, step, fn, scope){
36463         var cm = this.colModel, clen = cm.getColumnCount();
36464         var ds = this.dataSource, rlen = ds.getCount(), first = true;
36465         if(step < 0){
36466             if(col < 0){
36467                 row--;
36468                 first = false;
36469             }
36470             while(row >= 0){
36471                 if(!first){
36472                     col = clen-1;
36473                 }
36474                 first = false;
36475                 while(col >= 0){
36476                     if(fn.call(scope || this, row, col, cm) === true){
36477                         return [row, col];
36478                     }
36479                     col--;
36480                 }
36481                 row--;
36482             }
36483         } else {
36484             if(col >= clen){
36485                 row++;
36486                 first = false;
36487             }
36488             while(row < rlen){
36489                 if(!first){
36490                     col = 0;
36491                 }
36492                 first = false;
36493                 while(col < clen){
36494                     if(fn.call(scope || this, row, col, cm) === true){
36495                         return [row, col];
36496                     }
36497                     col++;
36498                 }
36499                 row++;
36500             }
36501         }
36502         return null;
36503     },
36504
36505     // private
36506     getSelections : function(){
36507         return this.selModel.getSelections();
36508     },
36509
36510     /**
36511      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
36512      * but if manual update is required this method will initiate it.
36513      */
36514     autoSize : function(){
36515         if(this.rendered){
36516             this.view.layout();
36517             if(this.view.adjustForScroll){
36518                 this.view.adjustForScroll();
36519             }
36520         }
36521     },
36522
36523     /**
36524      * Returns the grid's underlying element.
36525      * @return {Element} The element
36526      */
36527     getGridEl : function(){
36528         return this.container;
36529     },
36530
36531     // private for compatibility, overridden by editor grid
36532     stopEditing : function(){},
36533
36534     /**
36535      * Returns the grid's SelectionModel.
36536      * @return {SelectionModel}
36537      */
36538     getSelectionModel : function(){
36539         if(!this.selModel){
36540             this.selModel = new Roo.grid.RowSelectionModel();
36541         }
36542         return this.selModel;
36543     },
36544
36545     /**
36546      * Returns the grid's DataSource.
36547      * @return {DataSource}
36548      */
36549     getDataSource : function(){
36550         return this.dataSource;
36551     },
36552
36553     /**
36554      * Returns the grid's ColumnModel.
36555      * @return {ColumnModel}
36556      */
36557     getColumnModel : function(){
36558         return this.colModel;
36559     },
36560
36561     /**
36562      * Returns the grid's GridView object.
36563      * @return {GridView}
36564      */
36565     getView : function(){
36566         if(!this.view){
36567             this.view = new Roo.grid.GridView(this.viewConfig);
36568         }
36569         return this.view;
36570     },
36571     /**
36572      * Called to get grid's drag proxy text, by default returns this.ddText.
36573      * @return {String}
36574      */
36575     getDragDropText : function(){
36576         var count = this.selModel.getCount();
36577         return String.format(this.ddText, count, count == 1 ? '' : 's');
36578     }
36579 });
36580 /**
36581  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
36582  * %0 is replaced with the number of selected rows.
36583  * @type String
36584  */
36585 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
36586  * Based on:
36587  * Ext JS Library 1.1.1
36588  * Copyright(c) 2006-2007, Ext JS, LLC.
36589  *
36590  * Originally Released Under LGPL - original licence link has changed is not relivant.
36591  *
36592  * Fork - LGPL
36593  * <script type="text/javascript">
36594  */
36595  
36596 Roo.grid.AbstractGridView = function(){
36597         this.grid = null;
36598         
36599         this.events = {
36600             "beforerowremoved" : true,
36601             "beforerowsinserted" : true,
36602             "beforerefresh" : true,
36603             "rowremoved" : true,
36604             "rowsinserted" : true,
36605             "rowupdated" : true,
36606             "refresh" : true
36607         };
36608     Roo.grid.AbstractGridView.superclass.constructor.call(this);
36609 };
36610
36611 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
36612     rowClass : "x-grid-row",
36613     cellClass : "x-grid-cell",
36614     tdClass : "x-grid-td",
36615     hdClass : "x-grid-hd",
36616     splitClass : "x-grid-hd-split",
36617     
36618     init: function(grid){
36619         this.grid = grid;
36620                 var cid = this.grid.getGridEl().id;
36621         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
36622         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
36623         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
36624         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
36625         },
36626         
36627     getColumnRenderers : function(){
36628         var renderers = [];
36629         var cm = this.grid.colModel;
36630         var colCount = cm.getColumnCount();
36631         for(var i = 0; i < colCount; i++){
36632             renderers[i] = cm.getRenderer(i);
36633         }
36634         return renderers;
36635     },
36636     
36637     getColumnIds : function(){
36638         var ids = [];
36639         var cm = this.grid.colModel;
36640         var colCount = cm.getColumnCount();
36641         for(var i = 0; i < colCount; i++){
36642             ids[i] = cm.getColumnId(i);
36643         }
36644         return ids;
36645     },
36646     
36647     getDataIndexes : function(){
36648         if(!this.indexMap){
36649             this.indexMap = this.buildIndexMap();
36650         }
36651         return this.indexMap.colToData;
36652     },
36653     
36654     getColumnIndexByDataIndex : function(dataIndex){
36655         if(!this.indexMap){
36656             this.indexMap = this.buildIndexMap();
36657         }
36658         return this.indexMap.dataToCol[dataIndex];
36659     },
36660     
36661     /**
36662      * Set a css style for a column dynamically. 
36663      * @param {Number} colIndex The index of the column
36664      * @param {String} name The css property name
36665      * @param {String} value The css value
36666      */
36667     setCSSStyle : function(colIndex, name, value){
36668         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
36669         Roo.util.CSS.updateRule(selector, name, value);
36670     },
36671     
36672     generateRules : function(cm){
36673         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
36674         Roo.util.CSS.removeStyleSheet(rulesId);
36675         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36676             var cid = cm.getColumnId(i);
36677             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
36678                          this.tdSelector, cid, " {\n}\n",
36679                          this.hdSelector, cid, " {\n}\n",
36680                          this.splitSelector, cid, " {\n}\n");
36681         }
36682         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
36683     }
36684 });/*
36685  * Based on:
36686  * Ext JS Library 1.1.1
36687  * Copyright(c) 2006-2007, Ext JS, LLC.
36688  *
36689  * Originally Released Under LGPL - original licence link has changed is not relivant.
36690  *
36691  * Fork - LGPL
36692  * <script type="text/javascript">
36693  */
36694
36695 // private
36696 // This is a support class used internally by the Grid components
36697 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
36698     this.grid = grid;
36699     this.view = grid.getView();
36700     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36701     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
36702     if(hd2){
36703         this.setHandleElId(Roo.id(hd));
36704         this.setOuterHandleElId(Roo.id(hd2));
36705     }
36706     this.scroll = false;
36707 };
36708 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
36709     maxDragWidth: 120,
36710     getDragData : function(e){
36711         var t = Roo.lib.Event.getTarget(e);
36712         var h = this.view.findHeaderCell(t);
36713         if(h){
36714             return {ddel: h.firstChild, header:h};
36715         }
36716         return false;
36717     },
36718
36719     onInitDrag : function(e){
36720         this.view.headersDisabled = true;
36721         var clone = this.dragData.ddel.cloneNode(true);
36722         clone.id = Roo.id();
36723         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
36724         this.proxy.update(clone);
36725         return true;
36726     },
36727
36728     afterValidDrop : function(){
36729         var v = this.view;
36730         setTimeout(function(){
36731             v.headersDisabled = false;
36732         }, 50);
36733     },
36734
36735     afterInvalidDrop : function(){
36736         var v = this.view;
36737         setTimeout(function(){
36738             v.headersDisabled = false;
36739         }, 50);
36740     }
36741 });
36742 /*
36743  * Based on:
36744  * Ext JS Library 1.1.1
36745  * Copyright(c) 2006-2007, Ext JS, LLC.
36746  *
36747  * Originally Released Under LGPL - original licence link has changed is not relivant.
36748  *
36749  * Fork - LGPL
36750  * <script type="text/javascript">
36751  */
36752 // private
36753 // This is a support class used internally by the Grid components
36754 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
36755     this.grid = grid;
36756     this.view = grid.getView();
36757     // split the proxies so they don't interfere with mouse events
36758     this.proxyTop = Roo.DomHelper.append(document.body, {
36759         cls:"col-move-top", html:"&#160;"
36760     }, true);
36761     this.proxyBottom = Roo.DomHelper.append(document.body, {
36762         cls:"col-move-bottom", html:"&#160;"
36763     }, true);
36764     this.proxyTop.hide = this.proxyBottom.hide = function(){
36765         this.setLeftTop(-100,-100);
36766         this.setStyle("visibility", "hidden");
36767     };
36768     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36769     // temporarily disabled
36770     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
36771     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
36772 };
36773 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
36774     proxyOffsets : [-4, -9],
36775     fly: Roo.Element.fly,
36776
36777     getTargetFromEvent : function(e){
36778         var t = Roo.lib.Event.getTarget(e);
36779         var cindex = this.view.findCellIndex(t);
36780         if(cindex !== false){
36781             return this.view.getHeaderCell(cindex);
36782         }
36783         return null;
36784     },
36785
36786     nextVisible : function(h){
36787         var v = this.view, cm = this.grid.colModel;
36788         h = h.nextSibling;
36789         while(h){
36790             if(!cm.isHidden(v.getCellIndex(h))){
36791                 return h;
36792             }
36793             h = h.nextSibling;
36794         }
36795         return null;
36796     },
36797
36798     prevVisible : function(h){
36799         var v = this.view, cm = this.grid.colModel;
36800         h = h.prevSibling;
36801         while(h){
36802             if(!cm.isHidden(v.getCellIndex(h))){
36803                 return h;
36804             }
36805             h = h.prevSibling;
36806         }
36807         return null;
36808     },
36809
36810     positionIndicator : function(h, n, e){
36811         var x = Roo.lib.Event.getPageX(e);
36812         var r = Roo.lib.Dom.getRegion(n.firstChild);
36813         var px, pt, py = r.top + this.proxyOffsets[1];
36814         if((r.right - x) <= (r.right-r.left)/2){
36815             px = r.right+this.view.borderWidth;
36816             pt = "after";
36817         }else{
36818             px = r.left;
36819             pt = "before";
36820         }
36821         var oldIndex = this.view.getCellIndex(h);
36822         var newIndex = this.view.getCellIndex(n);
36823
36824         if(this.grid.colModel.isFixed(newIndex)){
36825             return false;
36826         }
36827
36828         var locked = this.grid.colModel.isLocked(newIndex);
36829
36830         if(pt == "after"){
36831             newIndex++;
36832         }
36833         if(oldIndex < newIndex){
36834             newIndex--;
36835         }
36836         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
36837             return false;
36838         }
36839         px +=  this.proxyOffsets[0];
36840         this.proxyTop.setLeftTop(px, py);
36841         this.proxyTop.show();
36842         if(!this.bottomOffset){
36843             this.bottomOffset = this.view.mainHd.getHeight();
36844         }
36845         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
36846         this.proxyBottom.show();
36847         return pt;
36848     },
36849
36850     onNodeEnter : function(n, dd, e, data){
36851         if(data.header != n){
36852             this.positionIndicator(data.header, n, e);
36853         }
36854     },
36855
36856     onNodeOver : function(n, dd, e, data){
36857         var result = false;
36858         if(data.header != n){
36859             result = this.positionIndicator(data.header, n, e);
36860         }
36861         if(!result){
36862             this.proxyTop.hide();
36863             this.proxyBottom.hide();
36864         }
36865         return result ? this.dropAllowed : this.dropNotAllowed;
36866     },
36867
36868     onNodeOut : function(n, dd, e, data){
36869         this.proxyTop.hide();
36870         this.proxyBottom.hide();
36871     },
36872
36873     onNodeDrop : function(n, dd, e, data){
36874         var h = data.header;
36875         if(h != n){
36876             var cm = this.grid.colModel;
36877             var x = Roo.lib.Event.getPageX(e);
36878             var r = Roo.lib.Dom.getRegion(n.firstChild);
36879             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
36880             var oldIndex = this.view.getCellIndex(h);
36881             var newIndex = this.view.getCellIndex(n);
36882             var locked = cm.isLocked(newIndex);
36883             if(pt == "after"){
36884                 newIndex++;
36885             }
36886             if(oldIndex < newIndex){
36887                 newIndex--;
36888             }
36889             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
36890                 return false;
36891             }
36892             cm.setLocked(oldIndex, locked, true);
36893             cm.moveColumn(oldIndex, newIndex);
36894             this.grid.fireEvent("columnmove", oldIndex, newIndex);
36895             return true;
36896         }
36897         return false;
36898     }
36899 });
36900 /*
36901  * Based on:
36902  * Ext JS Library 1.1.1
36903  * Copyright(c) 2006-2007, Ext JS, LLC.
36904  *
36905  * Originally Released Under LGPL - original licence link has changed is not relivant.
36906  *
36907  * Fork - LGPL
36908  * <script type="text/javascript">
36909  */
36910   
36911 /**
36912  * @class Roo.grid.GridView
36913  * @extends Roo.util.Observable
36914  *
36915  * @constructor
36916  * @param {Object} config
36917  */
36918 Roo.grid.GridView = function(config){
36919     Roo.grid.GridView.superclass.constructor.call(this);
36920     this.el = null;
36921
36922     Roo.apply(this, config);
36923 };
36924
36925 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
36926
36927     unselectable :  'unselectable="on"',
36928     unselectableCls :  'x-unselectable',
36929     
36930     
36931     rowClass : "x-grid-row",
36932
36933     cellClass : "x-grid-col",
36934
36935     tdClass : "x-grid-td",
36936
36937     hdClass : "x-grid-hd",
36938
36939     splitClass : "x-grid-split",
36940
36941     sortClasses : ["sort-asc", "sort-desc"],
36942
36943     enableMoveAnim : false,
36944
36945     hlColor: "C3DAF9",
36946
36947     dh : Roo.DomHelper,
36948
36949     fly : Roo.Element.fly,
36950
36951     css : Roo.util.CSS,
36952
36953     borderWidth: 1,
36954
36955     splitOffset: 3,
36956
36957     scrollIncrement : 22,
36958
36959     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
36960
36961     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
36962
36963     bind : function(ds, cm){
36964         if(this.ds){
36965             this.ds.un("load", this.onLoad, this);
36966             this.ds.un("datachanged", this.onDataChange, this);
36967             this.ds.un("add", this.onAdd, this);
36968             this.ds.un("remove", this.onRemove, this);
36969             this.ds.un("update", this.onUpdate, this);
36970             this.ds.un("clear", this.onClear, this);
36971         }
36972         if(ds){
36973             ds.on("load", this.onLoad, this);
36974             ds.on("datachanged", this.onDataChange, this);
36975             ds.on("add", this.onAdd, this);
36976             ds.on("remove", this.onRemove, this);
36977             ds.on("update", this.onUpdate, this);
36978             ds.on("clear", this.onClear, this);
36979         }
36980         this.ds = ds;
36981
36982         if(this.cm){
36983             this.cm.un("widthchange", this.onColWidthChange, this);
36984             this.cm.un("headerchange", this.onHeaderChange, this);
36985             this.cm.un("hiddenchange", this.onHiddenChange, this);
36986             this.cm.un("columnmoved", this.onColumnMove, this);
36987             this.cm.un("columnlockchange", this.onColumnLock, this);
36988         }
36989         if(cm){
36990             this.generateRules(cm);
36991             cm.on("widthchange", this.onColWidthChange, this);
36992             cm.on("headerchange", this.onHeaderChange, this);
36993             cm.on("hiddenchange", this.onHiddenChange, this);
36994             cm.on("columnmoved", this.onColumnMove, this);
36995             cm.on("columnlockchange", this.onColumnLock, this);
36996         }
36997         this.cm = cm;
36998     },
36999
37000     init: function(grid){
37001         Roo.grid.GridView.superclass.init.call(this, grid);
37002
37003         this.bind(grid.dataSource, grid.colModel);
37004
37005         grid.on("headerclick", this.handleHeaderClick, this);
37006
37007         if(grid.trackMouseOver){
37008             grid.on("mouseover", this.onRowOver, this);
37009             grid.on("mouseout", this.onRowOut, this);
37010         }
37011         grid.cancelTextSelection = function(){};
37012         this.gridId = grid.id;
37013
37014         var tpls = this.templates || {};
37015
37016         if(!tpls.master){
37017             tpls.master = new Roo.Template(
37018                '<div class="x-grid" hidefocus="true">',
37019                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
37020                   '<div class="x-grid-topbar"></div>',
37021                   '<div class="x-grid-scroller"><div></div></div>',
37022                   '<div class="x-grid-locked">',
37023                       '<div class="x-grid-header">{lockedHeader}</div>',
37024                       '<div class="x-grid-body">{lockedBody}</div>',
37025                   "</div>",
37026                   '<div class="x-grid-viewport">',
37027                       '<div class="x-grid-header">{header}</div>',
37028                       '<div class="x-grid-body">{body}</div>',
37029                   "</div>",
37030                   '<div class="x-grid-bottombar"></div>',
37031                  
37032                   '<div class="x-grid-resize-proxy">&#160;</div>',
37033                "</div>"
37034             );
37035             tpls.master.disableformats = true;
37036         }
37037
37038         if(!tpls.header){
37039             tpls.header = new Roo.Template(
37040                '<table border="0" cellspacing="0" cellpadding="0">',
37041                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
37042                "</table>{splits}"
37043             );
37044             tpls.header.disableformats = true;
37045         }
37046         tpls.header.compile();
37047
37048         if(!tpls.hcell){
37049             tpls.hcell = new Roo.Template(
37050                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
37051                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
37052                 "</div></td>"
37053              );
37054              tpls.hcell.disableFormats = true;
37055         }
37056         tpls.hcell.compile();
37057
37058         if(!tpls.hsplit){
37059             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
37060                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
37061             tpls.hsplit.disableFormats = true;
37062         }
37063         tpls.hsplit.compile();
37064
37065         if(!tpls.body){
37066             tpls.body = new Roo.Template(
37067                '<table border="0" cellspacing="0" cellpadding="0">',
37068                "<tbody>{rows}</tbody>",
37069                "</table>"
37070             );
37071             tpls.body.disableFormats = true;
37072         }
37073         tpls.body.compile();
37074
37075         if(!tpls.row){
37076             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
37077             tpls.row.disableFormats = true;
37078         }
37079         tpls.row.compile();
37080
37081         if(!tpls.cell){
37082             tpls.cell = new Roo.Template(
37083                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
37084                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
37085                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
37086                 "</td>"
37087             );
37088             tpls.cell.disableFormats = true;
37089         }
37090         tpls.cell.compile();
37091
37092         this.templates = tpls;
37093     },
37094
37095     // remap these for backwards compat
37096     onColWidthChange : function(){
37097         this.updateColumns.apply(this, arguments);
37098     },
37099     onHeaderChange : function(){
37100         this.updateHeaders.apply(this, arguments);
37101     }, 
37102     onHiddenChange : function(){
37103         this.handleHiddenChange.apply(this, arguments);
37104     },
37105     onColumnMove : function(){
37106         this.handleColumnMove.apply(this, arguments);
37107     },
37108     onColumnLock : function(){
37109         this.handleLockChange.apply(this, arguments);
37110     },
37111
37112     onDataChange : function(){
37113         this.refresh();
37114         this.updateHeaderSortState();
37115     },
37116
37117     onClear : function(){
37118         this.refresh();
37119     },
37120
37121     onUpdate : function(ds, record){
37122         this.refreshRow(record);
37123     },
37124
37125     refreshRow : function(record){
37126         var ds = this.ds, index;
37127         if(typeof record == 'number'){
37128             index = record;
37129             record = ds.getAt(index);
37130         }else{
37131             index = ds.indexOf(record);
37132         }
37133         this.insertRows(ds, index, index, true);
37134         this.onRemove(ds, record, index+1, true);
37135         this.syncRowHeights(index, index);
37136         this.layout();
37137         this.fireEvent("rowupdated", this, index, record);
37138     },
37139
37140     onAdd : function(ds, records, index){
37141         this.insertRows(ds, index, index + (records.length-1));
37142     },
37143
37144     onRemove : function(ds, record, index, isUpdate){
37145         if(isUpdate !== true){
37146             this.fireEvent("beforerowremoved", this, index, record);
37147         }
37148         var bt = this.getBodyTable(), lt = this.getLockedTable();
37149         if(bt.rows[index]){
37150             bt.firstChild.removeChild(bt.rows[index]);
37151         }
37152         if(lt.rows[index]){
37153             lt.firstChild.removeChild(lt.rows[index]);
37154         }
37155         if(isUpdate !== true){
37156             this.stripeRows(index);
37157             this.syncRowHeights(index, index);
37158             this.layout();
37159             this.fireEvent("rowremoved", this, index, record);
37160         }
37161     },
37162
37163     onLoad : function(){
37164         this.scrollToTop();
37165     },
37166
37167     /**
37168      * Scrolls the grid to the top
37169      */
37170     scrollToTop : function(){
37171         if(this.scroller){
37172             this.scroller.dom.scrollTop = 0;
37173             this.syncScroll();
37174         }
37175     },
37176
37177     /**
37178      * Gets a panel in the header of the grid that can be used for toolbars etc.
37179      * After modifying the contents of this panel a call to grid.autoSize() may be
37180      * required to register any changes in size.
37181      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
37182      * @return Roo.Element
37183      */
37184     getHeaderPanel : function(doShow){
37185         if(doShow){
37186             this.headerPanel.show();
37187         }
37188         return this.headerPanel;
37189     },
37190
37191     /**
37192      * Gets a panel in the footer of the grid that can be used for toolbars etc.
37193      * After modifying the contents of this panel a call to grid.autoSize() may be
37194      * required to register any changes in size.
37195      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
37196      * @return Roo.Element
37197      */
37198     getFooterPanel : function(doShow){
37199         if(doShow){
37200             this.footerPanel.show();
37201         }
37202         return this.footerPanel;
37203     },
37204
37205     initElements : function(){
37206         var E = Roo.Element;
37207         var el = this.grid.getGridEl().dom.firstChild;
37208         var cs = el.childNodes;
37209
37210         this.el = new E(el);
37211         
37212          this.focusEl = new E(el.firstChild);
37213         this.focusEl.swallowEvent("click", true);
37214         
37215         this.headerPanel = new E(cs[1]);
37216         this.headerPanel.enableDisplayMode("block");
37217
37218         this.scroller = new E(cs[2]);
37219         this.scrollSizer = new E(this.scroller.dom.firstChild);
37220
37221         this.lockedWrap = new E(cs[3]);
37222         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
37223         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
37224
37225         this.mainWrap = new E(cs[4]);
37226         this.mainHd = new E(this.mainWrap.dom.firstChild);
37227         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
37228
37229         this.footerPanel = new E(cs[5]);
37230         this.footerPanel.enableDisplayMode("block");
37231
37232         this.resizeProxy = new E(cs[6]);
37233
37234         this.headerSelector = String.format(
37235            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
37236            this.lockedHd.id, this.mainHd.id
37237         );
37238
37239         this.splitterSelector = String.format(
37240            '#{0} div.x-grid-split, #{1} div.x-grid-split',
37241            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
37242         );
37243     },
37244     idToCssName : function(s)
37245     {
37246         return s.replace(/[^a-z0-9]+/ig, '-');
37247     },
37248
37249     getHeaderCell : function(index){
37250         return Roo.DomQuery.select(this.headerSelector)[index];
37251     },
37252
37253     getHeaderCellMeasure : function(index){
37254         return this.getHeaderCell(index).firstChild;
37255     },
37256
37257     getHeaderCellText : function(index){
37258         return this.getHeaderCell(index).firstChild.firstChild;
37259     },
37260
37261     getLockedTable : function(){
37262         return this.lockedBody.dom.firstChild;
37263     },
37264
37265     getBodyTable : function(){
37266         return this.mainBody.dom.firstChild;
37267     },
37268
37269     getLockedRow : function(index){
37270         return this.getLockedTable().rows[index];
37271     },
37272
37273     getRow : function(index){
37274         return this.getBodyTable().rows[index];
37275     },
37276
37277     getRowComposite : function(index){
37278         if(!this.rowEl){
37279             this.rowEl = new Roo.CompositeElementLite();
37280         }
37281         var els = [], lrow, mrow;
37282         if(lrow = this.getLockedRow(index)){
37283             els.push(lrow);
37284         }
37285         if(mrow = this.getRow(index)){
37286             els.push(mrow);
37287         }
37288         this.rowEl.elements = els;
37289         return this.rowEl;
37290     },
37291     /**
37292      * Gets the 'td' of the cell
37293      * 
37294      * @param {Integer} rowIndex row to select
37295      * @param {Integer} colIndex column to select
37296      * 
37297      * @return {Object} 
37298      */
37299     getCell : function(rowIndex, colIndex){
37300         var locked = this.cm.getLockedCount();
37301         var source;
37302         if(colIndex < locked){
37303             source = this.lockedBody.dom.firstChild;
37304         }else{
37305             source = this.mainBody.dom.firstChild;
37306             colIndex -= locked;
37307         }
37308         return source.rows[rowIndex].childNodes[colIndex];
37309     },
37310
37311     getCellText : function(rowIndex, colIndex){
37312         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
37313     },
37314
37315     getCellBox : function(cell){
37316         var b = this.fly(cell).getBox();
37317         if(Roo.isOpera){ // opera fails to report the Y
37318             b.y = cell.offsetTop + this.mainBody.getY();
37319         }
37320         return b;
37321     },
37322
37323     getCellIndex : function(cell){
37324         var id = String(cell.className).match(this.cellRE);
37325         if(id){
37326             return parseInt(id[1], 10);
37327         }
37328         return 0;
37329     },
37330
37331     findHeaderIndex : function(n){
37332         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
37333         return r ? this.getCellIndex(r) : false;
37334     },
37335
37336     findHeaderCell : function(n){
37337         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
37338         return r ? r : false;
37339     },
37340
37341     findRowIndex : function(n){
37342         if(!n){
37343             return false;
37344         }
37345         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
37346         return r ? r.rowIndex : false;
37347     },
37348
37349     findCellIndex : function(node){
37350         var stop = this.el.dom;
37351         while(node && node != stop){
37352             if(this.findRE.test(node.className)){
37353                 return this.getCellIndex(node);
37354             }
37355             node = node.parentNode;
37356         }
37357         return false;
37358     },
37359
37360     getColumnId : function(index){
37361         return this.cm.getColumnId(index);
37362     },
37363
37364     getSplitters : function()
37365     {
37366         if(this.splitterSelector){
37367            return Roo.DomQuery.select(this.splitterSelector);
37368         }else{
37369             return null;
37370       }
37371     },
37372
37373     getSplitter : function(index){
37374         return this.getSplitters()[index];
37375     },
37376
37377     onRowOver : function(e, t){
37378         var row;
37379         if((row = this.findRowIndex(t)) !== false){
37380             this.getRowComposite(row).addClass("x-grid-row-over");
37381         }
37382     },
37383
37384     onRowOut : function(e, t){
37385         var row;
37386         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
37387             this.getRowComposite(row).removeClass("x-grid-row-over");
37388         }
37389     },
37390
37391     renderHeaders : function(){
37392         var cm = this.cm;
37393         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
37394         var cb = [], lb = [], sb = [], lsb = [], p = {};
37395         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37396             p.cellId = "x-grid-hd-0-" + i;
37397             p.splitId = "x-grid-csplit-0-" + i;
37398             p.id = cm.getColumnId(i);
37399             p.title = cm.getColumnTooltip(i) || "";
37400             p.value = cm.getColumnHeader(i) || "";
37401             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
37402             if(!cm.isLocked(i)){
37403                 cb[cb.length] = ct.apply(p);
37404                 sb[sb.length] = st.apply(p);
37405             }else{
37406                 lb[lb.length] = ct.apply(p);
37407                 lsb[lsb.length] = st.apply(p);
37408             }
37409         }
37410         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
37411                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
37412     },
37413
37414     updateHeaders : function(){
37415         var html = this.renderHeaders();
37416         this.lockedHd.update(html[0]);
37417         this.mainHd.update(html[1]);
37418     },
37419
37420     /**
37421      * Focuses the specified row.
37422      * @param {Number} row The row index
37423      */
37424     focusRow : function(row)
37425     {
37426         //Roo.log('GridView.focusRow');
37427         var x = this.scroller.dom.scrollLeft;
37428         this.focusCell(row, 0, false);
37429         this.scroller.dom.scrollLeft = x;
37430     },
37431
37432     /**
37433      * Focuses the specified cell.
37434      * @param {Number} row The row index
37435      * @param {Number} col The column index
37436      * @param {Boolean} hscroll false to disable horizontal scrolling
37437      */
37438     focusCell : function(row, col, hscroll)
37439     {
37440         //Roo.log('GridView.focusCell');
37441         var el = this.ensureVisible(row, col, hscroll);
37442         this.focusEl.alignTo(el, "tl-tl");
37443         if(Roo.isGecko){
37444             this.focusEl.focus();
37445         }else{
37446             this.focusEl.focus.defer(1, this.focusEl);
37447         }
37448     },
37449
37450     /**
37451      * Scrolls the specified cell into view
37452      * @param {Number} row The row index
37453      * @param {Number} col The column index
37454      * @param {Boolean} hscroll false to disable horizontal scrolling
37455      */
37456     ensureVisible : function(row, col, hscroll)
37457     {
37458         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
37459         //return null; //disable for testing.
37460         if(typeof row != "number"){
37461             row = row.rowIndex;
37462         }
37463         if(row < 0 && row >= this.ds.getCount()){
37464             return  null;
37465         }
37466         col = (col !== undefined ? col : 0);
37467         var cm = this.grid.colModel;
37468         while(cm.isHidden(col)){
37469             col++;
37470         }
37471
37472         var el = this.getCell(row, col);
37473         if(!el){
37474             return null;
37475         }
37476         var c = this.scroller.dom;
37477
37478         var ctop = parseInt(el.offsetTop, 10);
37479         var cleft = parseInt(el.offsetLeft, 10);
37480         var cbot = ctop + el.offsetHeight;
37481         var cright = cleft + el.offsetWidth;
37482         
37483         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
37484         var stop = parseInt(c.scrollTop, 10);
37485         var sleft = parseInt(c.scrollLeft, 10);
37486         var sbot = stop + ch;
37487         var sright = sleft + c.clientWidth;
37488         /*
37489         Roo.log('GridView.ensureVisible:' +
37490                 ' ctop:' + ctop +
37491                 ' c.clientHeight:' + c.clientHeight +
37492                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
37493                 ' stop:' + stop +
37494                 ' cbot:' + cbot +
37495                 ' sbot:' + sbot +
37496                 ' ch:' + ch  
37497                 );
37498         */
37499         if(ctop < stop){
37500              c.scrollTop = ctop;
37501             //Roo.log("set scrolltop to ctop DISABLE?");
37502         }else if(cbot > sbot){
37503             //Roo.log("set scrolltop to cbot-ch");
37504             c.scrollTop = cbot-ch;
37505         }
37506         
37507         if(hscroll !== false){
37508             if(cleft < sleft){
37509                 c.scrollLeft = cleft;
37510             }else if(cright > sright){
37511                 c.scrollLeft = cright-c.clientWidth;
37512             }
37513         }
37514          
37515         return el;
37516     },
37517
37518     updateColumns : function(){
37519         this.grid.stopEditing();
37520         var cm = this.grid.colModel, colIds = this.getColumnIds();
37521         //var totalWidth = cm.getTotalWidth();
37522         var pos = 0;
37523         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37524             //if(cm.isHidden(i)) continue;
37525             var w = cm.getColumnWidth(i);
37526             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37527             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37528         }
37529         this.updateSplitters();
37530     },
37531
37532     generateRules : function(cm){
37533         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
37534         Roo.util.CSS.removeStyleSheet(rulesId);
37535         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37536             var cid = cm.getColumnId(i);
37537             var align = '';
37538             if(cm.config[i].align){
37539                 align = 'text-align:'+cm.config[i].align+';';
37540             }
37541             var hidden = '';
37542             if(cm.isHidden(i)){
37543                 hidden = 'display:none;';
37544             }
37545             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
37546             ruleBuf.push(
37547                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
37548                     this.hdSelector, cid, " {\n", align, width, "}\n",
37549                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
37550                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
37551         }
37552         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37553     },
37554
37555     updateSplitters : function(){
37556         var cm = this.cm, s = this.getSplitters();
37557         if(s){ // splitters not created yet
37558             var pos = 0, locked = true;
37559             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37560                 if(cm.isHidden(i)) continue;
37561                 var w = cm.getColumnWidth(i); // make sure it's a number
37562                 if(!cm.isLocked(i) && locked){
37563                     pos = 0;
37564                     locked = false;
37565                 }
37566                 pos += w;
37567                 s[i].style.left = (pos-this.splitOffset) + "px";
37568             }
37569         }
37570     },
37571
37572     handleHiddenChange : function(colModel, colIndex, hidden){
37573         if(hidden){
37574             this.hideColumn(colIndex);
37575         }else{
37576             this.unhideColumn(colIndex);
37577         }
37578     },
37579
37580     hideColumn : function(colIndex){
37581         var cid = this.getColumnId(colIndex);
37582         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
37583         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
37584         if(Roo.isSafari){
37585             this.updateHeaders();
37586         }
37587         this.updateSplitters();
37588         this.layout();
37589     },
37590
37591     unhideColumn : function(colIndex){
37592         var cid = this.getColumnId(colIndex);
37593         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
37594         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
37595
37596         if(Roo.isSafari){
37597             this.updateHeaders();
37598         }
37599         this.updateSplitters();
37600         this.layout();
37601     },
37602
37603     insertRows : function(dm, firstRow, lastRow, isUpdate){
37604         if(firstRow == 0 && lastRow == dm.getCount()-1){
37605             this.refresh();
37606         }else{
37607             if(!isUpdate){
37608                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
37609             }
37610             var s = this.getScrollState();
37611             var markup = this.renderRows(firstRow, lastRow);
37612             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
37613             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
37614             this.restoreScroll(s);
37615             if(!isUpdate){
37616                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
37617                 this.syncRowHeights(firstRow, lastRow);
37618                 this.stripeRows(firstRow);
37619                 this.layout();
37620             }
37621         }
37622     },
37623
37624     bufferRows : function(markup, target, index){
37625         var before = null, trows = target.rows, tbody = target.tBodies[0];
37626         if(index < trows.length){
37627             before = trows[index];
37628         }
37629         var b = document.createElement("div");
37630         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
37631         var rows = b.firstChild.rows;
37632         for(var i = 0, len = rows.length; i < len; i++){
37633             if(before){
37634                 tbody.insertBefore(rows[0], before);
37635             }else{
37636                 tbody.appendChild(rows[0]);
37637             }
37638         }
37639         b.innerHTML = "";
37640         b = null;
37641     },
37642
37643     deleteRows : function(dm, firstRow, lastRow){
37644         if(dm.getRowCount()<1){
37645             this.fireEvent("beforerefresh", this);
37646             this.mainBody.update("");
37647             this.lockedBody.update("");
37648             this.fireEvent("refresh", this);
37649         }else{
37650             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
37651             var bt = this.getBodyTable();
37652             var tbody = bt.firstChild;
37653             var rows = bt.rows;
37654             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
37655                 tbody.removeChild(rows[firstRow]);
37656             }
37657             this.stripeRows(firstRow);
37658             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
37659         }
37660     },
37661
37662     updateRows : function(dataSource, firstRow, lastRow){
37663         var s = this.getScrollState();
37664         this.refresh();
37665         this.restoreScroll(s);
37666     },
37667
37668     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
37669         if(!noRefresh){
37670            this.refresh();
37671         }
37672         this.updateHeaderSortState();
37673     },
37674
37675     getScrollState : function(){
37676         
37677         var sb = this.scroller.dom;
37678         return {left: sb.scrollLeft, top: sb.scrollTop};
37679     },
37680
37681     stripeRows : function(startRow){
37682         if(!this.grid.stripeRows || this.ds.getCount() < 1){
37683             return;
37684         }
37685         startRow = startRow || 0;
37686         var rows = this.getBodyTable().rows;
37687         var lrows = this.getLockedTable().rows;
37688         var cls = ' x-grid-row-alt ';
37689         for(var i = startRow, len = rows.length; i < len; i++){
37690             var row = rows[i], lrow = lrows[i];
37691             var isAlt = ((i+1) % 2 == 0);
37692             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
37693             if(isAlt == hasAlt){
37694                 continue;
37695             }
37696             if(isAlt){
37697                 row.className += " x-grid-row-alt";
37698             }else{
37699                 row.className = row.className.replace("x-grid-row-alt", "");
37700             }
37701             if(lrow){
37702                 lrow.className = row.className;
37703             }
37704         }
37705     },
37706
37707     restoreScroll : function(state){
37708         //Roo.log('GridView.restoreScroll');
37709         var sb = this.scroller.dom;
37710         sb.scrollLeft = state.left;
37711         sb.scrollTop = state.top;
37712         this.syncScroll();
37713     },
37714
37715     syncScroll : function(){
37716         //Roo.log('GridView.syncScroll');
37717         var sb = this.scroller.dom;
37718         var sh = this.mainHd.dom;
37719         var bs = this.mainBody.dom;
37720         var lv = this.lockedBody.dom;
37721         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
37722         lv.scrollTop = bs.scrollTop = sb.scrollTop;
37723     },
37724
37725     handleScroll : function(e){
37726         this.syncScroll();
37727         var sb = this.scroller.dom;
37728         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
37729         e.stopEvent();
37730     },
37731
37732     handleWheel : function(e){
37733         var d = e.getWheelDelta();
37734         this.scroller.dom.scrollTop -= d*22;
37735         // set this here to prevent jumpy scrolling on large tables
37736         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
37737         e.stopEvent();
37738     },
37739
37740     renderRows : function(startRow, endRow){
37741         // pull in all the crap needed to render rows
37742         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
37743         var colCount = cm.getColumnCount();
37744
37745         if(ds.getCount() < 1){
37746             return ["", ""];
37747         }
37748
37749         // build a map for all the columns
37750         var cs = [];
37751         for(var i = 0; i < colCount; i++){
37752             var name = cm.getDataIndex(i);
37753             cs[i] = {
37754                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
37755                 renderer : cm.getRenderer(i),
37756                 id : cm.getColumnId(i),
37757                 locked : cm.isLocked(i)
37758             };
37759         }
37760
37761         startRow = startRow || 0;
37762         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
37763
37764         // records to render
37765         var rs = ds.getRange(startRow, endRow);
37766
37767         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
37768     },
37769
37770     // As much as I hate to duplicate code, this was branched because FireFox really hates
37771     // [].join("") on strings. The performance difference was substantial enough to
37772     // branch this function
37773     doRender : Roo.isGecko ?
37774             function(cs, rs, ds, startRow, colCount, stripe){
37775                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37776                 // buffers
37777                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37778                 
37779                 var hasListener = this.grid.hasListener('rowclass');
37780                 var rowcfg = {};
37781                 for(var j = 0, len = rs.length; j < len; j++){
37782                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
37783                     for(var i = 0; i < colCount; i++){
37784                         c = cs[i];
37785                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37786                         p.id = c.id;
37787                         p.css = p.attr = "";
37788                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37789                         if(p.value == undefined || p.value === "") p.value = "&#160;";
37790                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37791                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37792                         }
37793                         var markup = ct.apply(p);
37794                         if(!c.locked){
37795                             cb+= markup;
37796                         }else{
37797                             lcb+= markup;
37798                         }
37799                     }
37800                     var alt = [];
37801                     if(stripe && ((rowIndex+1) % 2 == 0)){
37802                         alt.push("x-grid-row-alt")
37803                     }
37804                     if(r.dirty){
37805                         alt.push(  " x-grid-dirty-row");
37806                     }
37807                     rp.cells = lcb;
37808                     if(this.getRowClass){
37809                         alt.push(this.getRowClass(r, rowIndex));
37810                     }
37811                     if (hasListener) {
37812                         rowcfg = {
37813                              
37814                             record: r,
37815                             rowIndex : rowIndex,
37816                             rowClass : ''
37817                         };
37818                         this.grid.fireEvent('rowclass', this, rowcfg);
37819                         alt.push(rowcfg.rowClass);
37820                     }
37821                     rp.alt = alt.join(" ");
37822                     lbuf+= rt.apply(rp);
37823                     rp.cells = cb;
37824                     buf+=  rt.apply(rp);
37825                 }
37826                 return [lbuf, buf];
37827             } :
37828             function(cs, rs, ds, startRow, colCount, stripe){
37829                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37830                 // buffers
37831                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37832                 var hasListener = this.grid.hasListener('rowclass');
37833  
37834                 var rowcfg = {};
37835                 for(var j = 0, len = rs.length; j < len; j++){
37836                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
37837                     for(var i = 0; i < colCount; i++){
37838                         c = cs[i];
37839                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37840                         p.id = c.id;
37841                         p.css = p.attr = "";
37842                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37843                         if(p.value == undefined || p.value === "") p.value = "&#160;";
37844                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37845                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37846                         }
37847                         
37848                         var markup = ct.apply(p);
37849                         if(!c.locked){
37850                             cb[cb.length] = markup;
37851                         }else{
37852                             lcb[lcb.length] = markup;
37853                         }
37854                     }
37855                     var alt = [];
37856                     if(stripe && ((rowIndex+1) % 2 == 0)){
37857                         alt.push( "x-grid-row-alt");
37858                     }
37859                     if(r.dirty){
37860                         alt.push(" x-grid-dirty-row");
37861                     }
37862                     rp.cells = lcb;
37863                     if(this.getRowClass){
37864                         alt.push( this.getRowClass(r, rowIndex));
37865                     }
37866                     if (hasListener) {
37867                         rowcfg = {
37868                              
37869                             record: r,
37870                             rowIndex : rowIndex,
37871                             rowClass : ''
37872                         };
37873                         this.grid.fireEvent('rowclass', this, rowcfg);
37874                         alt.push(rowcfg.rowClass);
37875                     }
37876                     rp.alt = alt.join(" ");
37877                     rp.cells = lcb.join("");
37878                     lbuf[lbuf.length] = rt.apply(rp);
37879                     rp.cells = cb.join("");
37880                     buf[buf.length] =  rt.apply(rp);
37881                 }
37882                 return [lbuf.join(""), buf.join("")];
37883             },
37884
37885     renderBody : function(){
37886         var markup = this.renderRows();
37887         var bt = this.templates.body;
37888         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
37889     },
37890
37891     /**
37892      * Refreshes the grid
37893      * @param {Boolean} headersToo
37894      */
37895     refresh : function(headersToo){
37896         this.fireEvent("beforerefresh", this);
37897         this.grid.stopEditing();
37898         var result = this.renderBody();
37899         this.lockedBody.update(result[0]);
37900         this.mainBody.update(result[1]);
37901         if(headersToo === true){
37902             this.updateHeaders();
37903             this.updateColumns();
37904             this.updateSplitters();
37905             this.updateHeaderSortState();
37906         }
37907         this.syncRowHeights();
37908         this.layout();
37909         this.fireEvent("refresh", this);
37910     },
37911
37912     handleColumnMove : function(cm, oldIndex, newIndex){
37913         this.indexMap = null;
37914         var s = this.getScrollState();
37915         this.refresh(true);
37916         this.restoreScroll(s);
37917         this.afterMove(newIndex);
37918     },
37919
37920     afterMove : function(colIndex){
37921         if(this.enableMoveAnim && Roo.enableFx){
37922             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
37923         }
37924         // if multisort - fix sortOrder, and reload..
37925         if (this.grid.dataSource.multiSort) {
37926             // the we can call sort again..
37927             var dm = this.grid.dataSource;
37928             var cm = this.grid.colModel;
37929             var so = [];
37930             for(var i = 0; i < cm.config.length; i++ ) {
37931                 
37932                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
37933                     continue; // dont' bother, it's not in sort list or being set.
37934                 }
37935                 
37936                 so.push(cm.config[i].dataIndex);
37937             };
37938             dm.sortOrder = so;
37939             dm.load(dm.lastOptions);
37940             
37941             
37942         }
37943         
37944     },
37945
37946     updateCell : function(dm, rowIndex, dataIndex){
37947         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
37948         if(typeof colIndex == "undefined"){ // not present in grid
37949             return;
37950         }
37951         var cm = this.grid.colModel;
37952         var cell = this.getCell(rowIndex, colIndex);
37953         var cellText = this.getCellText(rowIndex, colIndex);
37954
37955         var p = {
37956             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
37957             id : cm.getColumnId(colIndex),
37958             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
37959         };
37960         var renderer = cm.getRenderer(colIndex);
37961         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
37962         if(typeof val == "undefined" || val === "") val = "&#160;";
37963         cellText.innerHTML = val;
37964         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
37965         this.syncRowHeights(rowIndex, rowIndex);
37966     },
37967
37968     calcColumnWidth : function(colIndex, maxRowsToMeasure){
37969         var maxWidth = 0;
37970         if(this.grid.autoSizeHeaders){
37971             var h = this.getHeaderCellMeasure(colIndex);
37972             maxWidth = Math.max(maxWidth, h.scrollWidth);
37973         }
37974         var tb, index;
37975         if(this.cm.isLocked(colIndex)){
37976             tb = this.getLockedTable();
37977             index = colIndex;
37978         }else{
37979             tb = this.getBodyTable();
37980             index = colIndex - this.cm.getLockedCount();
37981         }
37982         if(tb && tb.rows){
37983             var rows = tb.rows;
37984             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
37985             for(var i = 0; i < stopIndex; i++){
37986                 var cell = rows[i].childNodes[index].firstChild;
37987                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
37988             }
37989         }
37990         return maxWidth + /*margin for error in IE*/ 5;
37991     },
37992     /**
37993      * Autofit a column to its content.
37994      * @param {Number} colIndex
37995      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
37996      */
37997      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
37998          if(this.cm.isHidden(colIndex)){
37999              return; // can't calc a hidden column
38000          }
38001         if(forceMinSize){
38002             var cid = this.cm.getColumnId(colIndex);
38003             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
38004            if(this.grid.autoSizeHeaders){
38005                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
38006            }
38007         }
38008         var newWidth = this.calcColumnWidth(colIndex);
38009         this.cm.setColumnWidth(colIndex,
38010             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
38011         if(!suppressEvent){
38012             this.grid.fireEvent("columnresize", colIndex, newWidth);
38013         }
38014     },
38015
38016     /**
38017      * Autofits all columns to their content and then expands to fit any extra space in the grid
38018      */
38019      autoSizeColumns : function(){
38020         var cm = this.grid.colModel;
38021         var colCount = cm.getColumnCount();
38022         for(var i = 0; i < colCount; i++){
38023             this.autoSizeColumn(i, true, true);
38024         }
38025         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
38026             this.fitColumns();
38027         }else{
38028             this.updateColumns();
38029             this.layout();
38030         }
38031     },
38032
38033     /**
38034      * Autofits all columns to the grid's width proportionate with their current size
38035      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
38036      */
38037     fitColumns : function(reserveScrollSpace){
38038         var cm = this.grid.colModel;
38039         var colCount = cm.getColumnCount();
38040         var cols = [];
38041         var width = 0;
38042         var i, w;
38043         for (i = 0; i < colCount; i++){
38044             if(!cm.isHidden(i) && !cm.isFixed(i)){
38045                 w = cm.getColumnWidth(i);
38046                 cols.push(i);
38047                 cols.push(w);
38048                 width += w;
38049             }
38050         }
38051         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
38052         if(reserveScrollSpace){
38053             avail -= 17;
38054         }
38055         var frac = (avail - cm.getTotalWidth())/width;
38056         while (cols.length){
38057             w = cols.pop();
38058             i = cols.pop();
38059             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
38060         }
38061         this.updateColumns();
38062         this.layout();
38063     },
38064
38065     onRowSelect : function(rowIndex){
38066         var row = this.getRowComposite(rowIndex);
38067         row.addClass("x-grid-row-selected");
38068     },
38069
38070     onRowDeselect : function(rowIndex){
38071         var row = this.getRowComposite(rowIndex);
38072         row.removeClass("x-grid-row-selected");
38073     },
38074
38075     onCellSelect : function(row, col){
38076         var cell = this.getCell(row, col);
38077         if(cell){
38078             Roo.fly(cell).addClass("x-grid-cell-selected");
38079         }
38080     },
38081
38082     onCellDeselect : function(row, col){
38083         var cell = this.getCell(row, col);
38084         if(cell){
38085             Roo.fly(cell).removeClass("x-grid-cell-selected");
38086         }
38087     },
38088
38089     updateHeaderSortState : function(){
38090         
38091         // sort state can be single { field: xxx, direction : yyy}
38092         // or   { xxx=>ASC , yyy : DESC ..... }
38093         
38094         var mstate = {};
38095         if (!this.ds.multiSort) { 
38096             var state = this.ds.getSortState();
38097             if(!state){
38098                 return;
38099             }
38100             mstate[state.field] = state.direction;
38101             // FIXME... - this is not used here.. but might be elsewhere..
38102             this.sortState = state;
38103             
38104         } else {
38105             mstate = this.ds.sortToggle;
38106         }
38107         //remove existing sort classes..
38108         
38109         var sc = this.sortClasses;
38110         var hds = this.el.select(this.headerSelector).removeClass(sc);
38111         
38112         for(var f in mstate) {
38113         
38114             var sortColumn = this.cm.findColumnIndex(f);
38115             
38116             if(sortColumn != -1){
38117                 var sortDir = mstate[f];        
38118                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
38119             }
38120         }
38121         
38122          
38123         
38124     },
38125
38126
38127     handleHeaderClick : function(g, index,e){
38128         
38129         Roo.log("header click");
38130         
38131         if (Roo.isTouch) {
38132             // touch events on header are handled by context
38133             this.handleHdCtx(g,index,e);
38134             return;
38135         }
38136         
38137         
38138         if(this.headersDisabled){
38139             return;
38140         }
38141         var dm = g.dataSource, cm = g.colModel;
38142         if(!cm.isSortable(index)){
38143             return;
38144         }
38145         g.stopEditing();
38146         
38147         if (dm.multiSort) {
38148             // update the sortOrder
38149             var so = [];
38150             for(var i = 0; i < cm.config.length; i++ ) {
38151                 
38152                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
38153                     continue; // dont' bother, it's not in sort list or being set.
38154                 }
38155                 
38156                 so.push(cm.config[i].dataIndex);
38157             };
38158             dm.sortOrder = so;
38159         }
38160         
38161         
38162         dm.sort(cm.getDataIndex(index));
38163     },
38164
38165
38166     destroy : function(){
38167         if(this.colMenu){
38168             this.colMenu.removeAll();
38169             Roo.menu.MenuMgr.unregister(this.colMenu);
38170             this.colMenu.getEl().remove();
38171             delete this.colMenu;
38172         }
38173         if(this.hmenu){
38174             this.hmenu.removeAll();
38175             Roo.menu.MenuMgr.unregister(this.hmenu);
38176             this.hmenu.getEl().remove();
38177             delete this.hmenu;
38178         }
38179         if(this.grid.enableColumnMove){
38180             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
38181             if(dds){
38182                 for(var dd in dds){
38183                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
38184                         var elid = dds[dd].dragElId;
38185                         dds[dd].unreg();
38186                         Roo.get(elid).remove();
38187                     } else if(dds[dd].config.isTarget){
38188                         dds[dd].proxyTop.remove();
38189                         dds[dd].proxyBottom.remove();
38190                         dds[dd].unreg();
38191                     }
38192                     if(Roo.dd.DDM.locationCache[dd]){
38193                         delete Roo.dd.DDM.locationCache[dd];
38194                     }
38195                 }
38196                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
38197             }
38198         }
38199         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
38200         this.bind(null, null);
38201         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
38202     },
38203
38204     handleLockChange : function(){
38205         this.refresh(true);
38206     },
38207
38208     onDenyColumnLock : function(){
38209
38210     },
38211
38212     onDenyColumnHide : function(){
38213
38214     },
38215
38216     handleHdMenuClick : function(item){
38217         var index = this.hdCtxIndex;
38218         var cm = this.cm, ds = this.ds;
38219         switch(item.id){
38220             case "asc":
38221                 ds.sort(cm.getDataIndex(index), "ASC");
38222                 break;
38223             case "desc":
38224                 ds.sort(cm.getDataIndex(index), "DESC");
38225                 break;
38226             case "lock":
38227                 var lc = cm.getLockedCount();
38228                 if(cm.getColumnCount(true) <= lc+1){
38229                     this.onDenyColumnLock();
38230                     return;
38231                 }
38232                 if(lc != index){
38233                     cm.setLocked(index, true, true);
38234                     cm.moveColumn(index, lc);
38235                     this.grid.fireEvent("columnmove", index, lc);
38236                 }else{
38237                     cm.setLocked(index, true);
38238                 }
38239             break;
38240             case "unlock":
38241                 var lc = cm.getLockedCount();
38242                 if((lc-1) != index){
38243                     cm.setLocked(index, false, true);
38244                     cm.moveColumn(index, lc-1);
38245                     this.grid.fireEvent("columnmove", index, lc-1);
38246                 }else{
38247                     cm.setLocked(index, false);
38248                 }
38249             break;
38250             case 'wider': // used to expand cols on touch..
38251             case 'narrow':
38252                 var cw = cm.getColumnWidth(index);
38253                 cw += (item.id == 'wider' ? 1 : -1) * 50;
38254                 cw = Math.max(0, cw);
38255                 cw = Math.min(cw,4000);
38256                 cm.setColumnWidth(index, cw);
38257                 break;
38258                 
38259             default:
38260                 index = cm.getIndexById(item.id.substr(4));
38261                 if(index != -1){
38262                     if(item.checked && cm.getColumnCount(true) <= 1){
38263                         this.onDenyColumnHide();
38264                         return false;
38265                     }
38266                     cm.setHidden(index, item.checked);
38267                 }
38268         }
38269         return true;
38270     },
38271
38272     beforeColMenuShow : function(){
38273         var cm = this.cm,  colCount = cm.getColumnCount();
38274         this.colMenu.removeAll();
38275         for(var i = 0; i < colCount; i++){
38276             this.colMenu.add(new Roo.menu.CheckItem({
38277                 id: "col-"+cm.getColumnId(i),
38278                 text: cm.getColumnHeader(i),
38279                 checked: !cm.isHidden(i),
38280                 hideOnClick:false
38281             }));
38282         }
38283     },
38284
38285     handleHdCtx : function(g, index, e){
38286         e.stopEvent();
38287         var hd = this.getHeaderCell(index);
38288         this.hdCtxIndex = index;
38289         var ms = this.hmenu.items, cm = this.cm;
38290         ms.get("asc").setDisabled(!cm.isSortable(index));
38291         ms.get("desc").setDisabled(!cm.isSortable(index));
38292         if(this.grid.enableColLock !== false){
38293             ms.get("lock").setDisabled(cm.isLocked(index));
38294             ms.get("unlock").setDisabled(!cm.isLocked(index));
38295         }
38296         this.hmenu.show(hd, "tl-bl");
38297     },
38298
38299     handleHdOver : function(e){
38300         var hd = this.findHeaderCell(e.getTarget());
38301         if(hd && !this.headersDisabled){
38302             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
38303                this.fly(hd).addClass("x-grid-hd-over");
38304             }
38305         }
38306     },
38307
38308     handleHdOut : function(e){
38309         var hd = this.findHeaderCell(e.getTarget());
38310         if(hd){
38311             this.fly(hd).removeClass("x-grid-hd-over");
38312         }
38313     },
38314
38315     handleSplitDblClick : function(e, t){
38316         var i = this.getCellIndex(t);
38317         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
38318             this.autoSizeColumn(i, true);
38319             this.layout();
38320         }
38321     },
38322
38323     render : function(){
38324
38325         var cm = this.cm;
38326         var colCount = cm.getColumnCount();
38327
38328         if(this.grid.monitorWindowResize === true){
38329             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38330         }
38331         var header = this.renderHeaders();
38332         var body = this.templates.body.apply({rows:""});
38333         var html = this.templates.master.apply({
38334             lockedBody: body,
38335             body: body,
38336             lockedHeader: header[0],
38337             header: header[1]
38338         });
38339
38340         //this.updateColumns();
38341
38342         this.grid.getGridEl().dom.innerHTML = html;
38343
38344         this.initElements();
38345         
38346         // a kludge to fix the random scolling effect in webkit
38347         this.el.on("scroll", function() {
38348             this.el.dom.scrollTop=0; // hopefully not recursive..
38349         },this);
38350
38351         this.scroller.on("scroll", this.handleScroll, this);
38352         this.lockedBody.on("mousewheel", this.handleWheel, this);
38353         this.mainBody.on("mousewheel", this.handleWheel, this);
38354
38355         this.mainHd.on("mouseover", this.handleHdOver, this);
38356         this.mainHd.on("mouseout", this.handleHdOut, this);
38357         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
38358                 {delegate: "."+this.splitClass});
38359
38360         this.lockedHd.on("mouseover", this.handleHdOver, this);
38361         this.lockedHd.on("mouseout", this.handleHdOut, this);
38362         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
38363                 {delegate: "."+this.splitClass});
38364
38365         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
38366             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38367         }
38368
38369         this.updateSplitters();
38370
38371         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
38372             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38373             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38374         }
38375
38376         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
38377             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
38378             this.hmenu.add(
38379                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
38380                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
38381             );
38382             if(this.grid.enableColLock !== false){
38383                 this.hmenu.add('-',
38384                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
38385                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
38386                 );
38387             }
38388             if (Roo.isTouch) {
38389                  this.hmenu.add('-',
38390                     {id:"wider", text: this.columnsWiderText},
38391                     {id:"narrow", text: this.columnsNarrowText }
38392                 );
38393                 
38394                  
38395             }
38396             
38397             if(this.grid.enableColumnHide !== false){
38398
38399                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
38400                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
38401                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
38402
38403                 this.hmenu.add('-',
38404                     {id:"columns", text: this.columnsText, menu: this.colMenu}
38405                 );
38406             }
38407             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
38408
38409             this.grid.on("headercontextmenu", this.handleHdCtx, this);
38410         }
38411
38412         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
38413             this.dd = new Roo.grid.GridDragZone(this.grid, {
38414                 ddGroup : this.grid.ddGroup || 'GridDD'
38415             });
38416             
38417         }
38418
38419         /*
38420         for(var i = 0; i < colCount; i++){
38421             if(cm.isHidden(i)){
38422                 this.hideColumn(i);
38423             }
38424             if(cm.config[i].align){
38425                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
38426                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
38427             }
38428         }*/
38429         
38430         this.updateHeaderSortState();
38431
38432         this.beforeInitialResize();
38433         this.layout(true);
38434
38435         // two part rendering gives faster view to the user
38436         this.renderPhase2.defer(1, this);
38437     },
38438
38439     renderPhase2 : function(){
38440         // render the rows now
38441         this.refresh();
38442         if(this.grid.autoSizeColumns){
38443             this.autoSizeColumns();
38444         }
38445     },
38446
38447     beforeInitialResize : function(){
38448
38449     },
38450
38451     onColumnSplitterMoved : function(i, w){
38452         this.userResized = true;
38453         var cm = this.grid.colModel;
38454         cm.setColumnWidth(i, w, true);
38455         var cid = cm.getColumnId(i);
38456         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
38457         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
38458         this.updateSplitters();
38459         this.layout();
38460         this.grid.fireEvent("columnresize", i, w);
38461     },
38462
38463     syncRowHeights : function(startIndex, endIndex){
38464         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
38465             startIndex = startIndex || 0;
38466             var mrows = this.getBodyTable().rows;
38467             var lrows = this.getLockedTable().rows;
38468             var len = mrows.length-1;
38469             endIndex = Math.min(endIndex || len, len);
38470             for(var i = startIndex; i <= endIndex; i++){
38471                 var m = mrows[i], l = lrows[i];
38472                 var h = Math.max(m.offsetHeight, l.offsetHeight);
38473                 m.style.height = l.style.height = h + "px";
38474             }
38475         }
38476     },
38477
38478     layout : function(initialRender, is2ndPass){
38479         var g = this.grid;
38480         var auto = g.autoHeight;
38481         var scrollOffset = 16;
38482         var c = g.getGridEl(), cm = this.cm,
38483                 expandCol = g.autoExpandColumn,
38484                 gv = this;
38485         //c.beginMeasure();
38486
38487         if(!c.dom.offsetWidth){ // display:none?
38488             if(initialRender){
38489                 this.lockedWrap.show();
38490                 this.mainWrap.show();
38491             }
38492             return;
38493         }
38494
38495         var hasLock = this.cm.isLocked(0);
38496
38497         var tbh = this.headerPanel.getHeight();
38498         var bbh = this.footerPanel.getHeight();
38499
38500         if(auto){
38501             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
38502             var newHeight = ch + c.getBorderWidth("tb");
38503             if(g.maxHeight){
38504                 newHeight = Math.min(g.maxHeight, newHeight);
38505             }
38506             c.setHeight(newHeight);
38507         }
38508
38509         if(g.autoWidth){
38510             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
38511         }
38512
38513         var s = this.scroller;
38514
38515         var csize = c.getSize(true);
38516
38517         this.el.setSize(csize.width, csize.height);
38518
38519         this.headerPanel.setWidth(csize.width);
38520         this.footerPanel.setWidth(csize.width);
38521
38522         var hdHeight = this.mainHd.getHeight();
38523         var vw = csize.width;
38524         var vh = csize.height - (tbh + bbh);
38525
38526         s.setSize(vw, vh);
38527
38528         var bt = this.getBodyTable();
38529         var ltWidth = hasLock ?
38530                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
38531
38532         var scrollHeight = bt.offsetHeight;
38533         var scrollWidth = ltWidth + bt.offsetWidth;
38534         var vscroll = false, hscroll = false;
38535
38536         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
38537
38538         var lw = this.lockedWrap, mw = this.mainWrap;
38539         var lb = this.lockedBody, mb = this.mainBody;
38540
38541         setTimeout(function(){
38542             var t = s.dom.offsetTop;
38543             var w = s.dom.clientWidth,
38544                 h = s.dom.clientHeight;
38545
38546             lw.setTop(t);
38547             lw.setSize(ltWidth, h);
38548
38549             mw.setLeftTop(ltWidth, t);
38550             mw.setSize(w-ltWidth, h);
38551
38552             lb.setHeight(h-hdHeight);
38553             mb.setHeight(h-hdHeight);
38554
38555             if(is2ndPass !== true && !gv.userResized && expandCol){
38556                 // high speed resize without full column calculation
38557                 
38558                 var ci = cm.getIndexById(expandCol);
38559                 if (ci < 0) {
38560                     ci = cm.findColumnIndex(expandCol);
38561                 }
38562                 ci = Math.max(0, ci); // make sure it's got at least the first col.
38563                 var expandId = cm.getColumnId(ci);
38564                 var  tw = cm.getTotalWidth(false);
38565                 var currentWidth = cm.getColumnWidth(ci);
38566                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
38567                 if(currentWidth != cw){
38568                     cm.setColumnWidth(ci, cw, true);
38569                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38570                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38571                     gv.updateSplitters();
38572                     gv.layout(false, true);
38573                 }
38574             }
38575
38576             if(initialRender){
38577                 lw.show();
38578                 mw.show();
38579             }
38580             //c.endMeasure();
38581         }, 10);
38582     },
38583
38584     onWindowResize : function(){
38585         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
38586             return;
38587         }
38588         this.layout();
38589     },
38590
38591     appendFooter : function(parentEl){
38592         return null;
38593     },
38594
38595     sortAscText : "Sort Ascending",
38596     sortDescText : "Sort Descending",
38597     lockText : "Lock Column",
38598     unlockText : "Unlock Column",
38599     columnsText : "Columns",
38600  
38601     columnsWiderText : "Wider",
38602     columnsNarrowText : "Thinner"
38603 });
38604
38605
38606 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
38607     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
38608     this.proxy.el.addClass('x-grid3-col-dd');
38609 };
38610
38611 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
38612     handleMouseDown : function(e){
38613
38614     },
38615
38616     callHandleMouseDown : function(e){
38617         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
38618     }
38619 });
38620 /*
38621  * Based on:
38622  * Ext JS Library 1.1.1
38623  * Copyright(c) 2006-2007, Ext JS, LLC.
38624  *
38625  * Originally Released Under LGPL - original licence link has changed is not relivant.
38626  *
38627  * Fork - LGPL
38628  * <script type="text/javascript">
38629  */
38630  
38631 // private
38632 // This is a support class used internally by the Grid components
38633 Roo.grid.SplitDragZone = function(grid, hd, hd2){
38634     this.grid = grid;
38635     this.view = grid.getView();
38636     this.proxy = this.view.resizeProxy;
38637     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
38638         "gridSplitters" + this.grid.getGridEl().id, {
38639         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
38640     });
38641     this.setHandleElId(Roo.id(hd));
38642     this.setOuterHandleElId(Roo.id(hd2));
38643     this.scroll = false;
38644 };
38645 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
38646     fly: Roo.Element.fly,
38647
38648     b4StartDrag : function(x, y){
38649         this.view.headersDisabled = true;
38650         this.proxy.setHeight(this.view.mainWrap.getHeight());
38651         var w = this.cm.getColumnWidth(this.cellIndex);
38652         var minw = Math.max(w-this.grid.minColumnWidth, 0);
38653         this.resetConstraints();
38654         this.setXConstraint(minw, 1000);
38655         this.setYConstraint(0, 0);
38656         this.minX = x - minw;
38657         this.maxX = x + 1000;
38658         this.startPos = x;
38659         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
38660     },
38661
38662
38663     handleMouseDown : function(e){
38664         ev = Roo.EventObject.setEvent(e);
38665         var t = this.fly(ev.getTarget());
38666         if(t.hasClass("x-grid-split")){
38667             this.cellIndex = this.view.getCellIndex(t.dom);
38668             this.split = t.dom;
38669             this.cm = this.grid.colModel;
38670             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
38671                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
38672             }
38673         }
38674     },
38675
38676     endDrag : function(e){
38677         this.view.headersDisabled = false;
38678         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
38679         var diff = endX - this.startPos;
38680         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
38681     },
38682
38683     autoOffset : function(){
38684         this.setDelta(0,0);
38685     }
38686 });/*
38687  * Based on:
38688  * Ext JS Library 1.1.1
38689  * Copyright(c) 2006-2007, Ext JS, LLC.
38690  *
38691  * Originally Released Under LGPL - original licence link has changed is not relivant.
38692  *
38693  * Fork - LGPL
38694  * <script type="text/javascript">
38695  */
38696  
38697 // private
38698 // This is a support class used internally by the Grid components
38699 Roo.grid.GridDragZone = function(grid, config){
38700     this.view = grid.getView();
38701     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
38702     if(this.view.lockedBody){
38703         this.setHandleElId(Roo.id(this.view.mainBody.dom));
38704         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
38705     }
38706     this.scroll = false;
38707     this.grid = grid;
38708     this.ddel = document.createElement('div');
38709     this.ddel.className = 'x-grid-dd-wrap';
38710 };
38711
38712 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
38713     ddGroup : "GridDD",
38714
38715     getDragData : function(e){
38716         var t = Roo.lib.Event.getTarget(e);
38717         var rowIndex = this.view.findRowIndex(t);
38718         var sm = this.grid.selModel;
38719             
38720         //Roo.log(rowIndex);
38721         
38722         if (sm.getSelectedCell) {
38723             // cell selection..
38724             if (!sm.getSelectedCell()) {
38725                 return false;
38726             }
38727             if (rowIndex != sm.getSelectedCell()[0]) {
38728                 return false;
38729             }
38730         
38731         }
38732         
38733         if(rowIndex !== false){
38734             
38735             // if editorgrid.. 
38736             
38737             
38738             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
38739                
38740             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
38741               //  
38742             //}
38743             if (e.hasModifier()){
38744                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
38745             }
38746             
38747             Roo.log("getDragData");
38748             
38749             return {
38750                 grid: this.grid,
38751                 ddel: this.ddel,
38752                 rowIndex: rowIndex,
38753                 selections:sm.getSelections ? sm.getSelections() : (
38754                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
38755                 )
38756             };
38757         }
38758         return false;
38759     },
38760
38761     onInitDrag : function(e){
38762         var data = this.dragData;
38763         this.ddel.innerHTML = this.grid.getDragDropText();
38764         this.proxy.update(this.ddel);
38765         // fire start drag?
38766     },
38767
38768     afterRepair : function(){
38769         this.dragging = false;
38770     },
38771
38772     getRepairXY : function(e, data){
38773         return false;
38774     },
38775
38776     onEndDrag : function(data, e){
38777         // fire end drag?
38778     },
38779
38780     onValidDrop : function(dd, e, id){
38781         // fire drag drop?
38782         this.hideProxy();
38783     },
38784
38785     beforeInvalidDrop : function(e, id){
38786
38787     }
38788 });/*
38789  * Based on:
38790  * Ext JS Library 1.1.1
38791  * Copyright(c) 2006-2007, Ext JS, LLC.
38792  *
38793  * Originally Released Under LGPL - original licence link has changed is not relivant.
38794  *
38795  * Fork - LGPL
38796  * <script type="text/javascript">
38797  */
38798  
38799
38800 /**
38801  * @class Roo.grid.ColumnModel
38802  * @extends Roo.util.Observable
38803  * This is the default implementation of a ColumnModel used by the Grid. It defines
38804  * the columns in the grid.
38805  * <br>Usage:<br>
38806  <pre><code>
38807  var colModel = new Roo.grid.ColumnModel([
38808         {header: "Ticker", width: 60, sortable: true, locked: true},
38809         {header: "Company Name", width: 150, sortable: true},
38810         {header: "Market Cap.", width: 100, sortable: true},
38811         {header: "$ Sales", width: 100, sortable: true, renderer: money},
38812         {header: "Employees", width: 100, sortable: true, resizable: false}
38813  ]);
38814  </code></pre>
38815  * <p>
38816  
38817  * The config options listed for this class are options which may appear in each
38818  * individual column definition.
38819  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
38820  * @constructor
38821  * @param {Object} config An Array of column config objects. See this class's
38822  * config objects for details.
38823 */
38824 Roo.grid.ColumnModel = function(config){
38825         /**
38826      * The config passed into the constructor
38827      */
38828     this.config = config;
38829     this.lookup = {};
38830
38831     // if no id, create one
38832     // if the column does not have a dataIndex mapping,
38833     // map it to the order it is in the config
38834     for(var i = 0, len = config.length; i < len; i++){
38835         var c = config[i];
38836         if(typeof c.dataIndex == "undefined"){
38837             c.dataIndex = i;
38838         }
38839         if(typeof c.renderer == "string"){
38840             c.renderer = Roo.util.Format[c.renderer];
38841         }
38842         if(typeof c.id == "undefined"){
38843             c.id = Roo.id();
38844         }
38845         if(c.editor && c.editor.xtype){
38846             c.editor  = Roo.factory(c.editor, Roo.grid);
38847         }
38848         if(c.editor && c.editor.isFormField){
38849             c.editor = new Roo.grid.GridEditor(c.editor);
38850         }
38851         this.lookup[c.id] = c;
38852     }
38853
38854     /**
38855      * The width of columns which have no width specified (defaults to 100)
38856      * @type Number
38857      */
38858     this.defaultWidth = 100;
38859
38860     /**
38861      * Default sortable of columns which have no sortable specified (defaults to false)
38862      * @type Boolean
38863      */
38864     this.defaultSortable = false;
38865
38866     this.addEvents({
38867         /**
38868              * @event widthchange
38869              * Fires when the width of a column changes.
38870              * @param {ColumnModel} this
38871              * @param {Number} columnIndex The column index
38872              * @param {Number} newWidth The new width
38873              */
38874             "widthchange": true,
38875         /**
38876              * @event headerchange
38877              * Fires when the text of a header changes.
38878              * @param {ColumnModel} this
38879              * @param {Number} columnIndex The column index
38880              * @param {Number} newText The new header text
38881              */
38882             "headerchange": true,
38883         /**
38884              * @event hiddenchange
38885              * Fires when a column is hidden or "unhidden".
38886              * @param {ColumnModel} this
38887              * @param {Number} columnIndex The column index
38888              * @param {Boolean} hidden true if hidden, false otherwise
38889              */
38890             "hiddenchange": true,
38891             /**
38892          * @event columnmoved
38893          * Fires when a column is moved.
38894          * @param {ColumnModel} this
38895          * @param {Number} oldIndex
38896          * @param {Number} newIndex
38897          */
38898         "columnmoved" : true,
38899         /**
38900          * @event columlockchange
38901          * Fires when a column's locked state is changed
38902          * @param {ColumnModel} this
38903          * @param {Number} colIndex
38904          * @param {Boolean} locked true if locked
38905          */
38906         "columnlockchange" : true
38907     });
38908     Roo.grid.ColumnModel.superclass.constructor.call(this);
38909 };
38910 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
38911     /**
38912      * @cfg {String} header The header text to display in the Grid view.
38913      */
38914     /**
38915      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
38916      * {@link Roo.data.Record} definition from which to draw the column's value. If not
38917      * specified, the column's index is used as an index into the Record's data Array.
38918      */
38919     /**
38920      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
38921      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
38922      */
38923     /**
38924      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
38925      * Defaults to the value of the {@link #defaultSortable} property.
38926      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
38927      */
38928     /**
38929      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
38930      */
38931     /**
38932      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
38933      */
38934     /**
38935      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
38936      */
38937     /**
38938      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
38939      */
38940     /**
38941      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
38942      * given the cell's data value. See {@link #setRenderer}. If not specified, the
38943      * default renderer uses the raw data value. If an object is returned (bootstrap only)
38944      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
38945      */
38946        /**
38947      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
38948      */
38949     /**
38950      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
38951      */
38952     /**
38953      * @cfg {String} cursor (Optional)
38954      */
38955     /**
38956      * @cfg {String} tooltip (Optional)
38957      */
38958     /**
38959      * @cfg {Number} xs (Optional)
38960      */
38961     /**
38962      * @cfg {Number} sm (Optional)
38963      */
38964     /**
38965      * @cfg {Number} md (Optional)
38966      */
38967     /**
38968      * @cfg {Number} lg (Optional)
38969      */
38970     /**
38971      * Returns the id of the column at the specified index.
38972      * @param {Number} index The column index
38973      * @return {String} the id
38974      */
38975     getColumnId : function(index){
38976         return this.config[index].id;
38977     },
38978
38979     /**
38980      * Returns the column for a specified id.
38981      * @param {String} id The column id
38982      * @return {Object} the column
38983      */
38984     getColumnById : function(id){
38985         return this.lookup[id];
38986     },
38987
38988     
38989     /**
38990      * Returns the column for a specified dataIndex.
38991      * @param {String} dataIndex The column dataIndex
38992      * @return {Object|Boolean} the column or false if not found
38993      */
38994     getColumnByDataIndex: function(dataIndex){
38995         var index = this.findColumnIndex(dataIndex);
38996         return index > -1 ? this.config[index] : false;
38997     },
38998     
38999     /**
39000      * Returns the index for a specified column id.
39001      * @param {String} id The column id
39002      * @return {Number} the index, or -1 if not found
39003      */
39004     getIndexById : function(id){
39005         for(var i = 0, len = this.config.length; i < len; i++){
39006             if(this.config[i].id == id){
39007                 return i;
39008             }
39009         }
39010         return -1;
39011     },
39012     
39013     /**
39014      * Returns the index for a specified column dataIndex.
39015      * @param {String} dataIndex The column dataIndex
39016      * @return {Number} the index, or -1 if not found
39017      */
39018     
39019     findColumnIndex : function(dataIndex){
39020         for(var i = 0, len = this.config.length; i < len; i++){
39021             if(this.config[i].dataIndex == dataIndex){
39022                 return i;
39023             }
39024         }
39025         return -1;
39026     },
39027     
39028     
39029     moveColumn : function(oldIndex, newIndex){
39030         var c = this.config[oldIndex];
39031         this.config.splice(oldIndex, 1);
39032         this.config.splice(newIndex, 0, c);
39033         this.dataMap = null;
39034         this.fireEvent("columnmoved", this, oldIndex, newIndex);
39035     },
39036
39037     isLocked : function(colIndex){
39038         return this.config[colIndex].locked === true;
39039     },
39040
39041     setLocked : function(colIndex, value, suppressEvent){
39042         if(this.isLocked(colIndex) == value){
39043             return;
39044         }
39045         this.config[colIndex].locked = value;
39046         if(!suppressEvent){
39047             this.fireEvent("columnlockchange", this, colIndex, value);
39048         }
39049     },
39050
39051     getTotalLockedWidth : function(){
39052         var totalWidth = 0;
39053         for(var i = 0; i < this.config.length; i++){
39054             if(this.isLocked(i) && !this.isHidden(i)){
39055                 this.totalWidth += this.getColumnWidth(i);
39056             }
39057         }
39058         return totalWidth;
39059     },
39060
39061     getLockedCount : function(){
39062         for(var i = 0, len = this.config.length; i < len; i++){
39063             if(!this.isLocked(i)){
39064                 return i;
39065             }
39066         }
39067     },
39068
39069     /**
39070      * Returns the number of columns.
39071      * @return {Number}
39072      */
39073     getColumnCount : function(visibleOnly){
39074         if(visibleOnly === true){
39075             var c = 0;
39076             for(var i = 0, len = this.config.length; i < len; i++){
39077                 if(!this.isHidden(i)){
39078                     c++;
39079                 }
39080             }
39081             return c;
39082         }
39083         return this.config.length;
39084     },
39085
39086     /**
39087      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
39088      * @param {Function} fn
39089      * @param {Object} scope (optional)
39090      * @return {Array} result
39091      */
39092     getColumnsBy : function(fn, scope){
39093         var r = [];
39094         for(var i = 0, len = this.config.length; i < len; i++){
39095             var c = this.config[i];
39096             if(fn.call(scope||this, c, i) === true){
39097                 r[r.length] = c;
39098             }
39099         }
39100         return r;
39101     },
39102
39103     /**
39104      * Returns true if the specified column is sortable.
39105      * @param {Number} col The column index
39106      * @return {Boolean}
39107      */
39108     isSortable : function(col){
39109         if(typeof this.config[col].sortable == "undefined"){
39110             return this.defaultSortable;
39111         }
39112         return this.config[col].sortable;
39113     },
39114
39115     /**
39116      * Returns the rendering (formatting) function defined for the column.
39117      * @param {Number} col The column index.
39118      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
39119      */
39120     getRenderer : function(col){
39121         if(!this.config[col].renderer){
39122             return Roo.grid.ColumnModel.defaultRenderer;
39123         }
39124         return this.config[col].renderer;
39125     },
39126
39127     /**
39128      * Sets the rendering (formatting) function for a column.
39129      * @param {Number} col The column index
39130      * @param {Function} fn The function to use to process the cell's raw data
39131      * to return HTML markup for the grid view. The render function is called with
39132      * the following parameters:<ul>
39133      * <li>Data value.</li>
39134      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
39135      * <li>css A CSS style string to apply to the table cell.</li>
39136      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
39137      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
39138      * <li>Row index</li>
39139      * <li>Column index</li>
39140      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
39141      */
39142     setRenderer : function(col, fn){
39143         this.config[col].renderer = fn;
39144     },
39145
39146     /**
39147      * Returns the width for the specified column.
39148      * @param {Number} col The column index
39149      * @return {Number}
39150      */
39151     getColumnWidth : function(col){
39152         return this.config[col].width * 1 || this.defaultWidth;
39153     },
39154
39155     /**
39156      * Sets the width for a column.
39157      * @param {Number} col The column index
39158      * @param {Number} width The new width
39159      */
39160     setColumnWidth : function(col, width, suppressEvent){
39161         this.config[col].width = width;
39162         this.totalWidth = null;
39163         if(!suppressEvent){
39164              this.fireEvent("widthchange", this, col, width);
39165         }
39166     },
39167
39168     /**
39169      * Returns the total width of all columns.
39170      * @param {Boolean} includeHidden True to include hidden column widths
39171      * @return {Number}
39172      */
39173     getTotalWidth : function(includeHidden){
39174         if(!this.totalWidth){
39175             this.totalWidth = 0;
39176             for(var i = 0, len = this.config.length; i < len; i++){
39177                 if(includeHidden || !this.isHidden(i)){
39178                     this.totalWidth += this.getColumnWidth(i);
39179                 }
39180             }
39181         }
39182         return this.totalWidth;
39183     },
39184
39185     /**
39186      * Returns the header for the specified column.
39187      * @param {Number} col The column index
39188      * @return {String}
39189      */
39190     getColumnHeader : function(col){
39191         return this.config[col].header;
39192     },
39193
39194     /**
39195      * Sets the header for a column.
39196      * @param {Number} col The column index
39197      * @param {String} header The new header
39198      */
39199     setColumnHeader : function(col, header){
39200         this.config[col].header = header;
39201         this.fireEvent("headerchange", this, col, header);
39202     },
39203
39204     /**
39205      * Returns the tooltip for the specified column.
39206      * @param {Number} col The column index
39207      * @return {String}
39208      */
39209     getColumnTooltip : function(col){
39210             return this.config[col].tooltip;
39211     },
39212     /**
39213      * Sets the tooltip for a column.
39214      * @param {Number} col The column index
39215      * @param {String} tooltip The new tooltip
39216      */
39217     setColumnTooltip : function(col, tooltip){
39218             this.config[col].tooltip = tooltip;
39219     },
39220
39221     /**
39222      * Returns the dataIndex for the specified column.
39223      * @param {Number} col The column index
39224      * @return {Number}
39225      */
39226     getDataIndex : function(col){
39227         return this.config[col].dataIndex;
39228     },
39229
39230     /**
39231      * Sets the dataIndex for a column.
39232      * @param {Number} col The column index
39233      * @param {Number} dataIndex The new dataIndex
39234      */
39235     setDataIndex : function(col, dataIndex){
39236         this.config[col].dataIndex = dataIndex;
39237     },
39238
39239     
39240     
39241     /**
39242      * Returns true if the cell is editable.
39243      * @param {Number} colIndex The column index
39244      * @param {Number} rowIndex The row index
39245      * @return {Boolean}
39246      */
39247     isCellEditable : function(colIndex, rowIndex){
39248         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
39249     },
39250
39251     /**
39252      * Returns the editor defined for the cell/column.
39253      * return false or null to disable editing.
39254      * @param {Number} colIndex The column index
39255      * @param {Number} rowIndex The row index
39256      * @return {Object}
39257      */
39258     getCellEditor : function(colIndex, rowIndex){
39259         return this.config[colIndex].editor;
39260     },
39261
39262     /**
39263      * Sets if a column is editable.
39264      * @param {Number} col The column index
39265      * @param {Boolean} editable True if the column is editable
39266      */
39267     setEditable : function(col, editable){
39268         this.config[col].editable = editable;
39269     },
39270
39271
39272     /**
39273      * Returns true if the column is hidden.
39274      * @param {Number} colIndex The column index
39275      * @return {Boolean}
39276      */
39277     isHidden : function(colIndex){
39278         return this.config[colIndex].hidden;
39279     },
39280
39281
39282     /**
39283      * Returns true if the column width cannot be changed
39284      */
39285     isFixed : function(colIndex){
39286         return this.config[colIndex].fixed;
39287     },
39288
39289     /**
39290      * Returns true if the column can be resized
39291      * @return {Boolean}
39292      */
39293     isResizable : function(colIndex){
39294         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
39295     },
39296     /**
39297      * Sets if a column is hidden.
39298      * @param {Number} colIndex The column index
39299      * @param {Boolean} hidden True if the column is hidden
39300      */
39301     setHidden : function(colIndex, hidden){
39302         this.config[colIndex].hidden = hidden;
39303         this.totalWidth = null;
39304         this.fireEvent("hiddenchange", this, colIndex, hidden);
39305     },
39306
39307     /**
39308      * Sets the editor for a column.
39309      * @param {Number} col The column index
39310      * @param {Object} editor The editor object
39311      */
39312     setEditor : function(col, editor){
39313         this.config[col].editor = editor;
39314     }
39315 });
39316
39317 Roo.grid.ColumnModel.defaultRenderer = function(value){
39318         if(typeof value == "string" && value.length < 1){
39319             return "&#160;";
39320         }
39321         return value;
39322 };
39323
39324 // Alias for backwards compatibility
39325 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
39326 /*
39327  * Based on:
39328  * Ext JS Library 1.1.1
39329  * Copyright(c) 2006-2007, Ext JS, LLC.
39330  *
39331  * Originally Released Under LGPL - original licence link has changed is not relivant.
39332  *
39333  * Fork - LGPL
39334  * <script type="text/javascript">
39335  */
39336
39337 /**
39338  * @class Roo.grid.AbstractSelectionModel
39339  * @extends Roo.util.Observable
39340  * Abstract base class for grid SelectionModels.  It provides the interface that should be
39341  * implemented by descendant classes.  This class should not be directly instantiated.
39342  * @constructor
39343  */
39344 Roo.grid.AbstractSelectionModel = function(){
39345     this.locked = false;
39346     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
39347 };
39348
39349 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
39350     /** @ignore Called by the grid automatically. Do not call directly. */
39351     init : function(grid){
39352         this.grid = grid;
39353         this.initEvents();
39354     },
39355
39356     /**
39357      * Locks the selections.
39358      */
39359     lock : function(){
39360         this.locked = true;
39361     },
39362
39363     /**
39364      * Unlocks the selections.
39365      */
39366     unlock : function(){
39367         this.locked = false;
39368     },
39369
39370     /**
39371      * Returns true if the selections are locked.
39372      * @return {Boolean}
39373      */
39374     isLocked : function(){
39375         return this.locked;
39376     }
39377 });/*
39378  * Based on:
39379  * Ext JS Library 1.1.1
39380  * Copyright(c) 2006-2007, Ext JS, LLC.
39381  *
39382  * Originally Released Under LGPL - original licence link has changed is not relivant.
39383  *
39384  * Fork - LGPL
39385  * <script type="text/javascript">
39386  */
39387 /**
39388  * @extends Roo.grid.AbstractSelectionModel
39389  * @class Roo.grid.RowSelectionModel
39390  * The default SelectionModel used by {@link Roo.grid.Grid}.
39391  * It supports multiple selections and keyboard selection/navigation. 
39392  * @constructor
39393  * @param {Object} config
39394  */
39395 Roo.grid.RowSelectionModel = function(config){
39396     Roo.apply(this, config);
39397     this.selections = new Roo.util.MixedCollection(false, function(o){
39398         return o.id;
39399     });
39400
39401     this.last = false;
39402     this.lastActive = false;
39403
39404     this.addEvents({
39405         /**
39406              * @event selectionchange
39407              * Fires when the selection changes
39408              * @param {SelectionModel} this
39409              */
39410             "selectionchange" : true,
39411         /**
39412              * @event afterselectionchange
39413              * Fires after the selection changes (eg. by key press or clicking)
39414              * @param {SelectionModel} this
39415              */
39416             "afterselectionchange" : true,
39417         /**
39418              * @event beforerowselect
39419              * Fires when a row is selected being selected, return false to cancel.
39420              * @param {SelectionModel} this
39421              * @param {Number} rowIndex The selected index
39422              * @param {Boolean} keepExisting False if other selections will be cleared
39423              */
39424             "beforerowselect" : true,
39425         /**
39426              * @event rowselect
39427              * Fires when a row is selected.
39428              * @param {SelectionModel} this
39429              * @param {Number} rowIndex The selected index
39430              * @param {Roo.data.Record} r The record
39431              */
39432             "rowselect" : true,
39433         /**
39434              * @event rowdeselect
39435              * Fires when a row is deselected.
39436              * @param {SelectionModel} this
39437              * @param {Number} rowIndex The selected index
39438              */
39439         "rowdeselect" : true
39440     });
39441     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
39442     this.locked = false;
39443 };
39444
39445 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
39446     /**
39447      * @cfg {Boolean} singleSelect
39448      * True to allow selection of only one row at a time (defaults to false)
39449      */
39450     singleSelect : false,
39451
39452     // private
39453     initEvents : function(){
39454
39455         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
39456             this.grid.on("mousedown", this.handleMouseDown, this);
39457         }else{ // allow click to work like normal
39458             this.grid.on("rowclick", this.handleDragableRowClick, this);
39459         }
39460
39461         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
39462             "up" : function(e){
39463                 if(!e.shiftKey){
39464                     this.selectPrevious(e.shiftKey);
39465                 }else if(this.last !== false && this.lastActive !== false){
39466                     var last = this.last;
39467                     this.selectRange(this.last,  this.lastActive-1);
39468                     this.grid.getView().focusRow(this.lastActive);
39469                     if(last !== false){
39470                         this.last = last;
39471                     }
39472                 }else{
39473                     this.selectFirstRow();
39474                 }
39475                 this.fireEvent("afterselectionchange", this);
39476             },
39477             "down" : function(e){
39478                 if(!e.shiftKey){
39479                     this.selectNext(e.shiftKey);
39480                 }else if(this.last !== false && this.lastActive !== false){
39481                     var last = this.last;
39482                     this.selectRange(this.last,  this.lastActive+1);
39483                     this.grid.getView().focusRow(this.lastActive);
39484                     if(last !== false){
39485                         this.last = last;
39486                     }
39487                 }else{
39488                     this.selectFirstRow();
39489                 }
39490                 this.fireEvent("afterselectionchange", this);
39491             },
39492             scope: this
39493         });
39494
39495         var view = this.grid.view;
39496         view.on("refresh", this.onRefresh, this);
39497         view.on("rowupdated", this.onRowUpdated, this);
39498         view.on("rowremoved", this.onRemove, this);
39499     },
39500
39501     // private
39502     onRefresh : function(){
39503         var ds = this.grid.dataSource, i, v = this.grid.view;
39504         var s = this.selections;
39505         s.each(function(r){
39506             if((i = ds.indexOfId(r.id)) != -1){
39507                 v.onRowSelect(i);
39508                 s.add(ds.getAt(i)); // updating the selection relate data
39509             }else{
39510                 s.remove(r);
39511             }
39512         });
39513     },
39514
39515     // private
39516     onRemove : function(v, index, r){
39517         this.selections.remove(r);
39518     },
39519
39520     // private
39521     onRowUpdated : function(v, index, r){
39522         if(this.isSelected(r)){
39523             v.onRowSelect(index);
39524         }
39525     },
39526
39527     /**
39528      * Select records.
39529      * @param {Array} records The records to select
39530      * @param {Boolean} keepExisting (optional) True to keep existing selections
39531      */
39532     selectRecords : function(records, keepExisting){
39533         if(!keepExisting){
39534             this.clearSelections();
39535         }
39536         var ds = this.grid.dataSource;
39537         for(var i = 0, len = records.length; i < len; i++){
39538             this.selectRow(ds.indexOf(records[i]), true);
39539         }
39540     },
39541
39542     /**
39543      * Gets the number of selected rows.
39544      * @return {Number}
39545      */
39546     getCount : function(){
39547         return this.selections.length;
39548     },
39549
39550     /**
39551      * Selects the first row in the grid.
39552      */
39553     selectFirstRow : function(){
39554         this.selectRow(0);
39555     },
39556
39557     /**
39558      * Select the last row.
39559      * @param {Boolean} keepExisting (optional) True to keep existing selections
39560      */
39561     selectLastRow : function(keepExisting){
39562         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
39563     },
39564
39565     /**
39566      * Selects the row immediately following the last selected row.
39567      * @param {Boolean} keepExisting (optional) True to keep existing selections
39568      */
39569     selectNext : function(keepExisting){
39570         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
39571             this.selectRow(this.last+1, keepExisting);
39572             this.grid.getView().focusRow(this.last);
39573         }
39574     },
39575
39576     /**
39577      * Selects the row that precedes the last selected row.
39578      * @param {Boolean} keepExisting (optional) True to keep existing selections
39579      */
39580     selectPrevious : function(keepExisting){
39581         if(this.last){
39582             this.selectRow(this.last-1, keepExisting);
39583             this.grid.getView().focusRow(this.last);
39584         }
39585     },
39586
39587     /**
39588      * Returns the selected records
39589      * @return {Array} Array of selected records
39590      */
39591     getSelections : function(){
39592         return [].concat(this.selections.items);
39593     },
39594
39595     /**
39596      * Returns the first selected record.
39597      * @return {Record}
39598      */
39599     getSelected : function(){
39600         return this.selections.itemAt(0);
39601     },
39602
39603
39604     /**
39605      * Clears all selections.
39606      */
39607     clearSelections : function(fast){
39608         if(this.locked) return;
39609         if(fast !== true){
39610             var ds = this.grid.dataSource;
39611             var s = this.selections;
39612             s.each(function(r){
39613                 this.deselectRow(ds.indexOfId(r.id));
39614             }, this);
39615             s.clear();
39616         }else{
39617             this.selections.clear();
39618         }
39619         this.last = false;
39620     },
39621
39622
39623     /**
39624      * Selects all rows.
39625      */
39626     selectAll : function(){
39627         if(this.locked) return;
39628         this.selections.clear();
39629         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
39630             this.selectRow(i, true);
39631         }
39632     },
39633
39634     /**
39635      * Returns True if there is a selection.
39636      * @return {Boolean}
39637      */
39638     hasSelection : function(){
39639         return this.selections.length > 0;
39640     },
39641
39642     /**
39643      * Returns True if the specified row is selected.
39644      * @param {Number/Record} record The record or index of the record to check
39645      * @return {Boolean}
39646      */
39647     isSelected : function(index){
39648         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
39649         return (r && this.selections.key(r.id) ? true : false);
39650     },
39651
39652     /**
39653      * Returns True if the specified record id is selected.
39654      * @param {String} id The id of record to check
39655      * @return {Boolean}
39656      */
39657     isIdSelected : function(id){
39658         return (this.selections.key(id) ? true : false);
39659     },
39660
39661     // private
39662     handleMouseDown : function(e, t){
39663         var view = this.grid.getView(), rowIndex;
39664         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
39665             return;
39666         };
39667         if(e.shiftKey && this.last !== false){
39668             var last = this.last;
39669             this.selectRange(last, rowIndex, e.ctrlKey);
39670             this.last = last; // reset the last
39671             view.focusRow(rowIndex);
39672         }else{
39673             var isSelected = this.isSelected(rowIndex);
39674             if(e.button !== 0 && isSelected){
39675                 view.focusRow(rowIndex);
39676             }else if(e.ctrlKey && isSelected){
39677                 this.deselectRow(rowIndex);
39678             }else if(!isSelected){
39679                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
39680                 view.focusRow(rowIndex);
39681             }
39682         }
39683         this.fireEvent("afterselectionchange", this);
39684     },
39685     // private
39686     handleDragableRowClick :  function(grid, rowIndex, e) 
39687     {
39688         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
39689             this.selectRow(rowIndex, false);
39690             grid.view.focusRow(rowIndex);
39691              this.fireEvent("afterselectionchange", this);
39692         }
39693     },
39694     
39695     /**
39696      * Selects multiple rows.
39697      * @param {Array} rows Array of the indexes of the row to select
39698      * @param {Boolean} keepExisting (optional) True to keep existing selections
39699      */
39700     selectRows : function(rows, keepExisting){
39701         if(!keepExisting){
39702             this.clearSelections();
39703         }
39704         for(var i = 0, len = rows.length; i < len; i++){
39705             this.selectRow(rows[i], true);
39706         }
39707     },
39708
39709     /**
39710      * Selects a range of rows. All rows in between startRow and endRow are also selected.
39711      * @param {Number} startRow The index of the first row in the range
39712      * @param {Number} endRow The index of the last row in the range
39713      * @param {Boolean} keepExisting (optional) True to retain existing selections
39714      */
39715     selectRange : function(startRow, endRow, keepExisting){
39716         if(this.locked) return;
39717         if(!keepExisting){
39718             this.clearSelections();
39719         }
39720         if(startRow <= endRow){
39721             for(var i = startRow; i <= endRow; i++){
39722                 this.selectRow(i, true);
39723             }
39724         }else{
39725             for(var i = startRow; i >= endRow; i--){
39726                 this.selectRow(i, true);
39727             }
39728         }
39729     },
39730
39731     /**
39732      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
39733      * @param {Number} startRow The index of the first row in the range
39734      * @param {Number} endRow The index of the last row in the range
39735      */
39736     deselectRange : function(startRow, endRow, preventViewNotify){
39737         if(this.locked) return;
39738         for(var i = startRow; i <= endRow; i++){
39739             this.deselectRow(i, preventViewNotify);
39740         }
39741     },
39742
39743     /**
39744      * Selects a row.
39745      * @param {Number} row The index of the row to select
39746      * @param {Boolean} keepExisting (optional) True to keep existing selections
39747      */
39748     selectRow : function(index, keepExisting, preventViewNotify){
39749         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
39750         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
39751             if(!keepExisting || this.singleSelect){
39752                 this.clearSelections();
39753             }
39754             var r = this.grid.dataSource.getAt(index);
39755             this.selections.add(r);
39756             this.last = this.lastActive = index;
39757             if(!preventViewNotify){
39758                 this.grid.getView().onRowSelect(index);
39759             }
39760             this.fireEvent("rowselect", this, index, r);
39761             this.fireEvent("selectionchange", this);
39762         }
39763     },
39764
39765     /**
39766      * Deselects a row.
39767      * @param {Number} row The index of the row to deselect
39768      */
39769     deselectRow : function(index, preventViewNotify){
39770         if(this.locked) return;
39771         if(this.last == index){
39772             this.last = false;
39773         }
39774         if(this.lastActive == index){
39775             this.lastActive = false;
39776         }
39777         var r = this.grid.dataSource.getAt(index);
39778         this.selections.remove(r);
39779         if(!preventViewNotify){
39780             this.grid.getView().onRowDeselect(index);
39781         }
39782         this.fireEvent("rowdeselect", this, index);
39783         this.fireEvent("selectionchange", this);
39784     },
39785
39786     // private
39787     restoreLast : function(){
39788         if(this._last){
39789             this.last = this._last;
39790         }
39791     },
39792
39793     // private
39794     acceptsNav : function(row, col, cm){
39795         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39796     },
39797
39798     // private
39799     onEditorKey : function(field, e){
39800         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
39801         if(k == e.TAB){
39802             e.stopEvent();
39803             ed.completeEdit();
39804             if(e.shiftKey){
39805                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39806             }else{
39807                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39808             }
39809         }else if(k == e.ENTER && !e.ctrlKey){
39810             e.stopEvent();
39811             ed.completeEdit();
39812             if(e.shiftKey){
39813                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
39814             }else{
39815                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
39816             }
39817         }else if(k == e.ESC){
39818             ed.cancelEdit();
39819         }
39820         if(newCell){
39821             g.startEditing(newCell[0], newCell[1]);
39822         }
39823     }
39824 });/*
39825  * Based on:
39826  * Ext JS Library 1.1.1
39827  * Copyright(c) 2006-2007, Ext JS, LLC.
39828  *
39829  * Originally Released Under LGPL - original licence link has changed is not relivant.
39830  *
39831  * Fork - LGPL
39832  * <script type="text/javascript">
39833  */
39834 /**
39835  * @class Roo.grid.CellSelectionModel
39836  * @extends Roo.grid.AbstractSelectionModel
39837  * This class provides the basic implementation for cell selection in a grid.
39838  * @constructor
39839  * @param {Object} config The object containing the configuration of this model.
39840  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
39841  */
39842 Roo.grid.CellSelectionModel = function(config){
39843     Roo.apply(this, config);
39844
39845     this.selection = null;
39846
39847     this.addEvents({
39848         /**
39849              * @event beforerowselect
39850              * Fires before a cell is selected.
39851              * @param {SelectionModel} this
39852              * @param {Number} rowIndex The selected row index
39853              * @param {Number} colIndex The selected cell index
39854              */
39855             "beforecellselect" : true,
39856         /**
39857              * @event cellselect
39858              * Fires when a cell is selected.
39859              * @param {SelectionModel} this
39860              * @param {Number} rowIndex The selected row index
39861              * @param {Number} colIndex The selected cell index
39862              */
39863             "cellselect" : true,
39864         /**
39865              * @event selectionchange
39866              * Fires when the active selection changes.
39867              * @param {SelectionModel} this
39868              * @param {Object} selection null for no selection or an object (o) with two properties
39869                 <ul>
39870                 <li>o.record: the record object for the row the selection is in</li>
39871                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
39872                 </ul>
39873              */
39874             "selectionchange" : true,
39875         /**
39876              * @event tabend
39877              * Fires when the tab (or enter) was pressed on the last editable cell
39878              * You can use this to trigger add new row.
39879              * @param {SelectionModel} this
39880              */
39881             "tabend" : true,
39882          /**
39883              * @event beforeeditnext
39884              * Fires before the next editable sell is made active
39885              * You can use this to skip to another cell or fire the tabend
39886              *    if you set cell to false
39887              * @param {Object} eventdata object : { cell : [ row, col ] } 
39888              */
39889             "beforeeditnext" : true
39890     });
39891     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
39892 };
39893
39894 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
39895     
39896     enter_is_tab: false,
39897
39898     /** @ignore */
39899     initEvents : function(){
39900         this.grid.on("mousedown", this.handleMouseDown, this);
39901         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
39902         var view = this.grid.view;
39903         view.on("refresh", this.onViewChange, this);
39904         view.on("rowupdated", this.onRowUpdated, this);
39905         view.on("beforerowremoved", this.clearSelections, this);
39906         view.on("beforerowsinserted", this.clearSelections, this);
39907         if(this.grid.isEditor){
39908             this.grid.on("beforeedit", this.beforeEdit,  this);
39909         }
39910     },
39911
39912         //private
39913     beforeEdit : function(e){
39914         this.select(e.row, e.column, false, true, e.record);
39915     },
39916
39917         //private
39918     onRowUpdated : function(v, index, r){
39919         if(this.selection && this.selection.record == r){
39920             v.onCellSelect(index, this.selection.cell[1]);
39921         }
39922     },
39923
39924         //private
39925     onViewChange : function(){
39926         this.clearSelections(true);
39927     },
39928
39929         /**
39930          * Returns the currently selected cell,.
39931          * @return {Array} The selected cell (row, column) or null if none selected.
39932          */
39933     getSelectedCell : function(){
39934         return this.selection ? this.selection.cell : null;
39935     },
39936
39937     /**
39938      * Clears all selections.
39939      * @param {Boolean} true to prevent the gridview from being notified about the change.
39940      */
39941     clearSelections : function(preventNotify){
39942         var s = this.selection;
39943         if(s){
39944             if(preventNotify !== true){
39945                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
39946             }
39947             this.selection = null;
39948             this.fireEvent("selectionchange", this, null);
39949         }
39950     },
39951
39952     /**
39953      * Returns true if there is a selection.
39954      * @return {Boolean}
39955      */
39956     hasSelection : function(){
39957         return this.selection ? true : false;
39958     },
39959
39960     /** @ignore */
39961     handleMouseDown : function(e, t){
39962         var v = this.grid.getView();
39963         if(this.isLocked()){
39964             return;
39965         };
39966         var row = v.findRowIndex(t);
39967         var cell = v.findCellIndex(t);
39968         if(row !== false && cell !== false){
39969             this.select(row, cell);
39970         }
39971     },
39972
39973     /**
39974      * Selects a cell.
39975      * @param {Number} rowIndex
39976      * @param {Number} collIndex
39977      */
39978     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
39979         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
39980             this.clearSelections();
39981             r = r || this.grid.dataSource.getAt(rowIndex);
39982             this.selection = {
39983                 record : r,
39984                 cell : [rowIndex, colIndex]
39985             };
39986             if(!preventViewNotify){
39987                 var v = this.grid.getView();
39988                 v.onCellSelect(rowIndex, colIndex);
39989                 if(preventFocus !== true){
39990                     v.focusCell(rowIndex, colIndex);
39991                 }
39992             }
39993             this.fireEvent("cellselect", this, rowIndex, colIndex);
39994             this.fireEvent("selectionchange", this, this.selection);
39995         }
39996     },
39997
39998         //private
39999     isSelectable : function(rowIndex, colIndex, cm){
40000         return !cm.isHidden(colIndex);
40001     },
40002
40003     /** @ignore */
40004     handleKeyDown : function(e){
40005         //Roo.log('Cell Sel Model handleKeyDown');
40006         if(!e.isNavKeyPress()){
40007             return;
40008         }
40009         var g = this.grid, s = this.selection;
40010         if(!s){
40011             e.stopEvent();
40012             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
40013             if(cell){
40014                 this.select(cell[0], cell[1]);
40015             }
40016             return;
40017         }
40018         var sm = this;
40019         var walk = function(row, col, step){
40020             return g.walkCells(row, col, step, sm.isSelectable,  sm);
40021         };
40022         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
40023         var newCell;
40024
40025       
40026
40027         switch(k){
40028             case e.TAB:
40029                 // handled by onEditorKey
40030                 if (g.isEditor && g.editing) {
40031                     return;
40032                 }
40033                 if(e.shiftKey) {
40034                     newCell = walk(r, c-1, -1);
40035                 } else {
40036                     newCell = walk(r, c+1, 1);
40037                 }
40038                 break;
40039             
40040             case e.DOWN:
40041                newCell = walk(r+1, c, 1);
40042                 break;
40043             
40044             case e.UP:
40045                 newCell = walk(r-1, c, -1);
40046                 break;
40047             
40048             case e.RIGHT:
40049                 newCell = walk(r, c+1, 1);
40050                 break;
40051             
40052             case e.LEFT:
40053                 newCell = walk(r, c-1, -1);
40054                 break;
40055             
40056             case e.ENTER:
40057                 
40058                 if(g.isEditor && !g.editing){
40059                    g.startEditing(r, c);
40060                    e.stopEvent();
40061                    return;
40062                 }
40063                 
40064                 
40065              break;
40066         };
40067         if(newCell){
40068             this.select(newCell[0], newCell[1]);
40069             e.stopEvent();
40070             
40071         }
40072     },
40073
40074     acceptsNav : function(row, col, cm){
40075         return !cm.isHidden(col) && cm.isCellEditable(col, row);
40076     },
40077     /**
40078      * Selects a cell.
40079      * @param {Number} field (not used) - as it's normally used as a listener
40080      * @param {Number} e - event - fake it by using
40081      *
40082      * var e = Roo.EventObjectImpl.prototype;
40083      * e.keyCode = e.TAB
40084      *
40085      * 
40086      */
40087     onEditorKey : function(field, e){
40088         
40089         var k = e.getKey(),
40090             newCell,
40091             g = this.grid,
40092             ed = g.activeEditor,
40093             forward = false;
40094         ///Roo.log('onEditorKey' + k);
40095         
40096         
40097         if (this.enter_is_tab && k == e.ENTER) {
40098             k = e.TAB;
40099         }
40100         
40101         if(k == e.TAB){
40102             if(e.shiftKey){
40103                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
40104             }else{
40105                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
40106                 forward = true;
40107             }
40108             
40109             e.stopEvent();
40110             
40111         } else if(k == e.ENTER &&  !e.ctrlKey){
40112             ed.completeEdit();
40113             e.stopEvent();
40114             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
40115         
40116                 } else if(k == e.ESC){
40117             ed.cancelEdit();
40118         }
40119                 
40120         if (newCell) {
40121             var ecall = { cell : newCell, forward : forward };
40122             this.fireEvent('beforeeditnext', ecall );
40123             newCell = ecall.cell;
40124                         forward = ecall.forward;
40125         }
40126                 
40127         if(newCell){
40128             //Roo.log('next cell after edit');
40129             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
40130         } else if (forward) {
40131             // tabbed past last
40132             this.fireEvent.defer(100, this, ['tabend',this]);
40133         }
40134     }
40135 });/*
40136  * Based on:
40137  * Ext JS Library 1.1.1
40138  * Copyright(c) 2006-2007, Ext JS, LLC.
40139  *
40140  * Originally Released Under LGPL - original licence link has changed is not relivant.
40141  *
40142  * Fork - LGPL
40143  * <script type="text/javascript">
40144  */
40145  
40146 /**
40147  * @class Roo.grid.EditorGrid
40148  * @extends Roo.grid.Grid
40149  * Class for creating and editable grid.
40150  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
40151  * The container MUST have some type of size defined for the grid to fill. The container will be 
40152  * automatically set to position relative if it isn't already.
40153  * @param {Object} dataSource The data model to bind to
40154  * @param {Object} colModel The column model with info about this grid's columns
40155  */
40156 Roo.grid.EditorGrid = function(container, config){
40157     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
40158     this.getGridEl().addClass("xedit-grid");
40159
40160     if(!this.selModel){
40161         this.selModel = new Roo.grid.CellSelectionModel();
40162     }
40163
40164     this.activeEditor = null;
40165
40166         this.addEvents({
40167             /**
40168              * @event beforeedit
40169              * Fires before cell editing is triggered. The edit event object has the following properties <br />
40170              * <ul style="padding:5px;padding-left:16px;">
40171              * <li>grid - This grid</li>
40172              * <li>record - The record being edited</li>
40173              * <li>field - The field name being edited</li>
40174              * <li>value - The value for the field being edited.</li>
40175              * <li>row - The grid row index</li>
40176              * <li>column - The grid column index</li>
40177              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
40178              * </ul>
40179              * @param {Object} e An edit event (see above for description)
40180              */
40181             "beforeedit" : true,
40182             /**
40183              * @event afteredit
40184              * Fires after a cell is edited. <br />
40185              * <ul style="padding:5px;padding-left:16px;">
40186              * <li>grid - This grid</li>
40187              * <li>record - The record being edited</li>
40188              * <li>field - The field name being edited</li>
40189              * <li>value - The value being set</li>
40190              * <li>originalValue - The original value for the field, before the edit.</li>
40191              * <li>row - The grid row index</li>
40192              * <li>column - The grid column index</li>
40193              * </ul>
40194              * @param {Object} e An edit event (see above for description)
40195              */
40196             "afteredit" : true,
40197             /**
40198              * @event validateedit
40199              * Fires after a cell is edited, but before the value is set in the record. 
40200          * You can use this to modify the value being set in the field, Return false
40201              * to cancel the change. The edit event object has the following properties <br />
40202              * <ul style="padding:5px;padding-left:16px;">
40203          * <li>editor - This editor</li>
40204              * <li>grid - This grid</li>
40205              * <li>record - The record being edited</li>
40206              * <li>field - The field name being edited</li>
40207              * <li>value - The value being set</li>
40208              * <li>originalValue - The original value for the field, before the edit.</li>
40209              * <li>row - The grid row index</li>
40210              * <li>column - The grid column index</li>
40211              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
40212              * </ul>
40213              * @param {Object} e An edit event (see above for description)
40214              */
40215             "validateedit" : true
40216         });
40217     this.on("bodyscroll", this.stopEditing,  this);
40218     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
40219 };
40220
40221 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
40222     /**
40223      * @cfg {Number} clicksToEdit
40224      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
40225      */
40226     clicksToEdit: 2,
40227
40228     // private
40229     isEditor : true,
40230     // private
40231     trackMouseOver: false, // causes very odd FF errors
40232
40233     onCellDblClick : function(g, row, col){
40234         this.startEditing(row, col);
40235     },
40236
40237     onEditComplete : function(ed, value, startValue){
40238         this.editing = false;
40239         this.activeEditor = null;
40240         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
40241         var r = ed.record;
40242         var field = this.colModel.getDataIndex(ed.col);
40243         var e = {
40244             grid: this,
40245             record: r,
40246             field: field,
40247             originalValue: startValue,
40248             value: value,
40249             row: ed.row,
40250             column: ed.col,
40251             cancel:false,
40252             editor: ed
40253         };
40254         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
40255         cell.show();
40256           
40257         if(String(value) !== String(startValue)){
40258             
40259             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
40260                 r.set(field, e.value);
40261                 // if we are dealing with a combo box..
40262                 // then we also set the 'name' colum to be the displayField
40263                 if (ed.field.displayField && ed.field.name) {
40264                     r.set(ed.field.name, ed.field.el.dom.value);
40265                 }
40266                 
40267                 delete e.cancel; //?? why!!!
40268                 this.fireEvent("afteredit", e);
40269             }
40270         } else {
40271             this.fireEvent("afteredit", e); // always fire it!
40272         }
40273         this.view.focusCell(ed.row, ed.col);
40274     },
40275
40276     /**
40277      * Starts editing the specified for the specified row/column
40278      * @param {Number} rowIndex
40279      * @param {Number} colIndex
40280      */
40281     startEditing : function(row, col){
40282         this.stopEditing();
40283         if(this.colModel.isCellEditable(col, row)){
40284             this.view.ensureVisible(row, col, true);
40285           
40286             var r = this.dataSource.getAt(row);
40287             var field = this.colModel.getDataIndex(col);
40288             var cell = Roo.get(this.view.getCell(row,col));
40289             var e = {
40290                 grid: this,
40291                 record: r,
40292                 field: field,
40293                 value: r.data[field],
40294                 row: row,
40295                 column: col,
40296                 cancel:false 
40297             };
40298             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
40299                 this.editing = true;
40300                 var ed = this.colModel.getCellEditor(col, row);
40301                 
40302                 if (!ed) {
40303                     return;
40304                 }
40305                 if(!ed.rendered){
40306                     ed.render(ed.parentEl || document.body);
40307                 }
40308                 ed.field.reset();
40309                
40310                 cell.hide();
40311                 
40312                 (function(){ // complex but required for focus issues in safari, ie and opera
40313                     ed.row = row;
40314                     ed.col = col;
40315                     ed.record = r;
40316                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
40317                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
40318                     this.activeEditor = ed;
40319                     var v = r.data[field];
40320                     ed.startEdit(this.view.getCell(row, col), v);
40321                     // combo's with 'displayField and name set
40322                     if (ed.field.displayField && ed.field.name) {
40323                         ed.field.el.dom.value = r.data[ed.field.name];
40324                     }
40325                     
40326                     
40327                 }).defer(50, this);
40328             }
40329         }
40330     },
40331         
40332     /**
40333      * Stops any active editing
40334      */
40335     stopEditing : function(){
40336         if(this.activeEditor){
40337             this.activeEditor.completeEdit();
40338         }
40339         this.activeEditor = null;
40340     },
40341         
40342          /**
40343      * Called to get grid's drag proxy text, by default returns this.ddText.
40344      * @return {String}
40345      */
40346     getDragDropText : function(){
40347         var count = this.selModel.getSelectedCell() ? 1 : 0;
40348         return String.format(this.ddText, count, count == 1 ? '' : 's');
40349     }
40350         
40351 });/*
40352  * Based on:
40353  * Ext JS Library 1.1.1
40354  * Copyright(c) 2006-2007, Ext JS, LLC.
40355  *
40356  * Originally Released Under LGPL - original licence link has changed is not relivant.
40357  *
40358  * Fork - LGPL
40359  * <script type="text/javascript">
40360  */
40361
40362 // private - not really -- you end up using it !
40363 // This is a support class used internally by the Grid components
40364
40365 /**
40366  * @class Roo.grid.GridEditor
40367  * @extends Roo.Editor
40368  * Class for creating and editable grid elements.
40369  * @param {Object} config any settings (must include field)
40370  */
40371 Roo.grid.GridEditor = function(field, config){
40372     if (!config && field.field) {
40373         config = field;
40374         field = Roo.factory(config.field, Roo.form);
40375     }
40376     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
40377     field.monitorTab = false;
40378 };
40379
40380 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
40381     
40382     /**
40383      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
40384      */
40385     
40386     alignment: "tl-tl",
40387     autoSize: "width",
40388     hideEl : false,
40389     cls: "x-small-editor x-grid-editor",
40390     shim:false,
40391     shadow:"frame"
40392 });/*
40393  * Based on:
40394  * Ext JS Library 1.1.1
40395  * Copyright(c) 2006-2007, Ext JS, LLC.
40396  *
40397  * Originally Released Under LGPL - original licence link has changed is not relivant.
40398  *
40399  * Fork - LGPL
40400  * <script type="text/javascript">
40401  */
40402   
40403
40404   
40405 Roo.grid.PropertyRecord = Roo.data.Record.create([
40406     {name:'name',type:'string'},  'value'
40407 ]);
40408
40409
40410 Roo.grid.PropertyStore = function(grid, source){
40411     this.grid = grid;
40412     this.store = new Roo.data.Store({
40413         recordType : Roo.grid.PropertyRecord
40414     });
40415     this.store.on('update', this.onUpdate,  this);
40416     if(source){
40417         this.setSource(source);
40418     }
40419     Roo.grid.PropertyStore.superclass.constructor.call(this);
40420 };
40421
40422
40423
40424 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
40425     setSource : function(o){
40426         this.source = o;
40427         this.store.removeAll();
40428         var data = [];
40429         for(var k in o){
40430             if(this.isEditableValue(o[k])){
40431                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
40432             }
40433         }
40434         this.store.loadRecords({records: data}, {}, true);
40435     },
40436
40437     onUpdate : function(ds, record, type){
40438         if(type == Roo.data.Record.EDIT){
40439             var v = record.data['value'];
40440             var oldValue = record.modified['value'];
40441             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
40442                 this.source[record.id] = v;
40443                 record.commit();
40444                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
40445             }else{
40446                 record.reject();
40447             }
40448         }
40449     },
40450
40451     getProperty : function(row){
40452        return this.store.getAt(row);
40453     },
40454
40455     isEditableValue: function(val){
40456         if(val && val instanceof Date){
40457             return true;
40458         }else if(typeof val == 'object' || typeof val == 'function'){
40459             return false;
40460         }
40461         return true;
40462     },
40463
40464     setValue : function(prop, value){
40465         this.source[prop] = value;
40466         this.store.getById(prop).set('value', value);
40467     },
40468
40469     getSource : function(){
40470         return this.source;
40471     }
40472 });
40473
40474 Roo.grid.PropertyColumnModel = function(grid, store){
40475     this.grid = grid;
40476     var g = Roo.grid;
40477     g.PropertyColumnModel.superclass.constructor.call(this, [
40478         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
40479         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
40480     ]);
40481     this.store = store;
40482     this.bselect = Roo.DomHelper.append(document.body, {
40483         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
40484             {tag: 'option', value: 'true', html: 'true'},
40485             {tag: 'option', value: 'false', html: 'false'}
40486         ]
40487     });
40488     Roo.id(this.bselect);
40489     var f = Roo.form;
40490     this.editors = {
40491         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
40492         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
40493         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
40494         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
40495         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
40496     };
40497     this.renderCellDelegate = this.renderCell.createDelegate(this);
40498     this.renderPropDelegate = this.renderProp.createDelegate(this);
40499 };
40500
40501 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
40502     
40503     
40504     nameText : 'Name',
40505     valueText : 'Value',
40506     
40507     dateFormat : 'm/j/Y',
40508     
40509     
40510     renderDate : function(dateVal){
40511         return dateVal.dateFormat(this.dateFormat);
40512     },
40513
40514     renderBool : function(bVal){
40515         return bVal ? 'true' : 'false';
40516     },
40517
40518     isCellEditable : function(colIndex, rowIndex){
40519         return colIndex == 1;
40520     },
40521
40522     getRenderer : function(col){
40523         return col == 1 ?
40524             this.renderCellDelegate : this.renderPropDelegate;
40525     },
40526
40527     renderProp : function(v){
40528         return this.getPropertyName(v);
40529     },
40530
40531     renderCell : function(val){
40532         var rv = val;
40533         if(val instanceof Date){
40534             rv = this.renderDate(val);
40535         }else if(typeof val == 'boolean'){
40536             rv = this.renderBool(val);
40537         }
40538         return Roo.util.Format.htmlEncode(rv);
40539     },
40540
40541     getPropertyName : function(name){
40542         var pn = this.grid.propertyNames;
40543         return pn && pn[name] ? pn[name] : name;
40544     },
40545
40546     getCellEditor : function(colIndex, rowIndex){
40547         var p = this.store.getProperty(rowIndex);
40548         var n = p.data['name'], val = p.data['value'];
40549         
40550         if(typeof(this.grid.customEditors[n]) == 'string'){
40551             return this.editors[this.grid.customEditors[n]];
40552         }
40553         if(typeof(this.grid.customEditors[n]) != 'undefined'){
40554             return this.grid.customEditors[n];
40555         }
40556         if(val instanceof Date){
40557             return this.editors['date'];
40558         }else if(typeof val == 'number'){
40559             return this.editors['number'];
40560         }else if(typeof val == 'boolean'){
40561             return this.editors['boolean'];
40562         }else{
40563             return this.editors['string'];
40564         }
40565     }
40566 });
40567
40568 /**
40569  * @class Roo.grid.PropertyGrid
40570  * @extends Roo.grid.EditorGrid
40571  * This class represents the  interface of a component based property grid control.
40572  * <br><br>Usage:<pre><code>
40573  var grid = new Roo.grid.PropertyGrid("my-container-id", {
40574       
40575  });
40576  // set any options
40577  grid.render();
40578  * </code></pre>
40579   
40580  * @constructor
40581  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40582  * The container MUST have some type of size defined for the grid to fill. The container will be
40583  * automatically set to position relative if it isn't already.
40584  * @param {Object} config A config object that sets properties on this grid.
40585  */
40586 Roo.grid.PropertyGrid = function(container, config){
40587     config = config || {};
40588     var store = new Roo.grid.PropertyStore(this);
40589     this.store = store;
40590     var cm = new Roo.grid.PropertyColumnModel(this, store);
40591     store.store.sort('name', 'ASC');
40592     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
40593         ds: store.store,
40594         cm: cm,
40595         enableColLock:false,
40596         enableColumnMove:false,
40597         stripeRows:false,
40598         trackMouseOver: false,
40599         clicksToEdit:1
40600     }, config));
40601     this.getGridEl().addClass('x-props-grid');
40602     this.lastEditRow = null;
40603     this.on('columnresize', this.onColumnResize, this);
40604     this.addEvents({
40605          /**
40606              * @event beforepropertychange
40607              * Fires before a property changes (return false to stop?)
40608              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40609              * @param {String} id Record Id
40610              * @param {String} newval New Value
40611          * @param {String} oldval Old Value
40612              */
40613         "beforepropertychange": true,
40614         /**
40615              * @event propertychange
40616              * Fires after a property changes
40617              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40618              * @param {String} id Record Id
40619              * @param {String} newval New Value
40620          * @param {String} oldval Old Value
40621              */
40622         "propertychange": true
40623     });
40624     this.customEditors = this.customEditors || {};
40625 };
40626 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
40627     
40628      /**
40629      * @cfg {Object} customEditors map of colnames=> custom editors.
40630      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
40631      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
40632      * false disables editing of the field.
40633          */
40634     
40635       /**
40636      * @cfg {Object} propertyNames map of property Names to their displayed value
40637          */
40638     
40639     render : function(){
40640         Roo.grid.PropertyGrid.superclass.render.call(this);
40641         this.autoSize.defer(100, this);
40642     },
40643
40644     autoSize : function(){
40645         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
40646         if(this.view){
40647             this.view.fitColumns();
40648         }
40649     },
40650
40651     onColumnResize : function(){
40652         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
40653         this.autoSize();
40654     },
40655     /**
40656      * Sets the data for the Grid
40657      * accepts a Key => Value object of all the elements avaiable.
40658      * @param {Object} data  to appear in grid.
40659      */
40660     setSource : function(source){
40661         this.store.setSource(source);
40662         //this.autoSize();
40663     },
40664     /**
40665      * Gets all the data from the grid.
40666      * @return {Object} data  data stored in grid
40667      */
40668     getSource : function(){
40669         return this.store.getSource();
40670     }
40671 });/*
40672   
40673  * Licence LGPL
40674  
40675  */
40676  
40677 /**
40678  * @class Roo.grid.Calendar
40679  * @extends Roo.util.Grid
40680  * This class extends the Grid to provide a calendar widget
40681  * <br><br>Usage:<pre><code>
40682  var grid = new Roo.grid.Calendar("my-container-id", {
40683      ds: myDataStore,
40684      cm: myColModel,
40685      selModel: mySelectionModel,
40686      autoSizeColumns: true,
40687      monitorWindowResize: false,
40688      trackMouseOver: true
40689      eventstore : real data store..
40690  });
40691  // set any options
40692  grid.render();
40693   
40694   * @constructor
40695  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40696  * The container MUST have some type of size defined for the grid to fill. The container will be
40697  * automatically set to position relative if it isn't already.
40698  * @param {Object} config A config object that sets properties on this grid.
40699  */
40700 Roo.grid.Calendar = function(container, config){
40701         // initialize the container
40702         this.container = Roo.get(container);
40703         this.container.update("");
40704         this.container.setStyle("overflow", "hidden");
40705     this.container.addClass('x-grid-container');
40706
40707     this.id = this.container.id;
40708
40709     Roo.apply(this, config);
40710     // check and correct shorthanded configs
40711     
40712     var rows = [];
40713     var d =1;
40714     for (var r = 0;r < 6;r++) {
40715         
40716         rows[r]=[];
40717         for (var c =0;c < 7;c++) {
40718             rows[r][c]= '';
40719         }
40720     }
40721     if (this.eventStore) {
40722         this.eventStore= Roo.factory(this.eventStore, Roo.data);
40723         this.eventStore.on('load',this.onLoad, this);
40724         this.eventStore.on('beforeload',this.clearEvents, this);
40725          
40726     }
40727     
40728     this.dataSource = new Roo.data.Store({
40729             proxy: new Roo.data.MemoryProxy(rows),
40730             reader: new Roo.data.ArrayReader({}, [
40731                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
40732     });
40733
40734     this.dataSource.load();
40735     this.ds = this.dataSource;
40736     this.ds.xmodule = this.xmodule || false;
40737     
40738     
40739     var cellRender = function(v,x,r)
40740     {
40741         return String.format(
40742             '<div class="fc-day  fc-widget-content"><div>' +
40743                 '<div class="fc-event-container"></div>' +
40744                 '<div class="fc-day-number">{0}</div>'+
40745                 
40746                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
40747             '</div></div>', v);
40748     
40749     }
40750     
40751     
40752     this.colModel = new Roo.grid.ColumnModel( [
40753         {
40754             xtype: 'ColumnModel',
40755             xns: Roo.grid,
40756             dataIndex : 'weekday0',
40757             header : 'Sunday',
40758             renderer : cellRender
40759         },
40760         {
40761             xtype: 'ColumnModel',
40762             xns: Roo.grid,
40763             dataIndex : 'weekday1',
40764             header : 'Monday',
40765             renderer : cellRender
40766         },
40767         {
40768             xtype: 'ColumnModel',
40769             xns: Roo.grid,
40770             dataIndex : 'weekday2',
40771             header : 'Tuesday',
40772             renderer : cellRender
40773         },
40774         {
40775             xtype: 'ColumnModel',
40776             xns: Roo.grid,
40777             dataIndex : 'weekday3',
40778             header : 'Wednesday',
40779             renderer : cellRender
40780         },
40781         {
40782             xtype: 'ColumnModel',
40783             xns: Roo.grid,
40784             dataIndex : 'weekday4',
40785             header : 'Thursday',
40786             renderer : cellRender
40787         },
40788         {
40789             xtype: 'ColumnModel',
40790             xns: Roo.grid,
40791             dataIndex : 'weekday5',
40792             header : 'Friday',
40793             renderer : cellRender
40794         },
40795         {
40796             xtype: 'ColumnModel',
40797             xns: Roo.grid,
40798             dataIndex : 'weekday6',
40799             header : 'Saturday',
40800             renderer : cellRender
40801         }
40802     ]);
40803     this.cm = this.colModel;
40804     this.cm.xmodule = this.xmodule || false;
40805  
40806         
40807           
40808     //this.selModel = new Roo.grid.CellSelectionModel();
40809     //this.sm = this.selModel;
40810     //this.selModel.init(this);
40811     
40812     
40813     if(this.width){
40814         this.container.setWidth(this.width);
40815     }
40816
40817     if(this.height){
40818         this.container.setHeight(this.height);
40819     }
40820     /** @private */
40821         this.addEvents({
40822         // raw events
40823         /**
40824          * @event click
40825          * The raw click event for the entire grid.
40826          * @param {Roo.EventObject} e
40827          */
40828         "click" : true,
40829         /**
40830          * @event dblclick
40831          * The raw dblclick event for the entire grid.
40832          * @param {Roo.EventObject} e
40833          */
40834         "dblclick" : true,
40835         /**
40836          * @event contextmenu
40837          * The raw contextmenu event for the entire grid.
40838          * @param {Roo.EventObject} e
40839          */
40840         "contextmenu" : true,
40841         /**
40842          * @event mousedown
40843          * The raw mousedown event for the entire grid.
40844          * @param {Roo.EventObject} e
40845          */
40846         "mousedown" : true,
40847         /**
40848          * @event mouseup
40849          * The raw mouseup event for the entire grid.
40850          * @param {Roo.EventObject} e
40851          */
40852         "mouseup" : true,
40853         /**
40854          * @event mouseover
40855          * The raw mouseover event for the entire grid.
40856          * @param {Roo.EventObject} e
40857          */
40858         "mouseover" : true,
40859         /**
40860          * @event mouseout
40861          * The raw mouseout event for the entire grid.
40862          * @param {Roo.EventObject} e
40863          */
40864         "mouseout" : true,
40865         /**
40866          * @event keypress
40867          * The raw keypress event for the entire grid.
40868          * @param {Roo.EventObject} e
40869          */
40870         "keypress" : true,
40871         /**
40872          * @event keydown
40873          * The raw keydown event for the entire grid.
40874          * @param {Roo.EventObject} e
40875          */
40876         "keydown" : true,
40877
40878         // custom events
40879
40880         /**
40881          * @event cellclick
40882          * Fires when a cell is clicked
40883          * @param {Grid} this
40884          * @param {Number} rowIndex
40885          * @param {Number} columnIndex
40886          * @param {Roo.EventObject} e
40887          */
40888         "cellclick" : true,
40889         /**
40890          * @event celldblclick
40891          * Fires when a cell is double clicked
40892          * @param {Grid} this
40893          * @param {Number} rowIndex
40894          * @param {Number} columnIndex
40895          * @param {Roo.EventObject} e
40896          */
40897         "celldblclick" : true,
40898         /**
40899          * @event rowclick
40900          * Fires when a row is clicked
40901          * @param {Grid} this
40902          * @param {Number} rowIndex
40903          * @param {Roo.EventObject} e
40904          */
40905         "rowclick" : true,
40906         /**
40907          * @event rowdblclick
40908          * Fires when a row is double clicked
40909          * @param {Grid} this
40910          * @param {Number} rowIndex
40911          * @param {Roo.EventObject} e
40912          */
40913         "rowdblclick" : true,
40914         /**
40915          * @event headerclick
40916          * Fires when a header is clicked
40917          * @param {Grid} this
40918          * @param {Number} columnIndex
40919          * @param {Roo.EventObject} e
40920          */
40921         "headerclick" : true,
40922         /**
40923          * @event headerdblclick
40924          * Fires when a header cell is double clicked
40925          * @param {Grid} this
40926          * @param {Number} columnIndex
40927          * @param {Roo.EventObject} e
40928          */
40929         "headerdblclick" : true,
40930         /**
40931          * @event rowcontextmenu
40932          * Fires when a row is right clicked
40933          * @param {Grid} this
40934          * @param {Number} rowIndex
40935          * @param {Roo.EventObject} e
40936          */
40937         "rowcontextmenu" : true,
40938         /**
40939          * @event cellcontextmenu
40940          * Fires when a cell is right clicked
40941          * @param {Grid} this
40942          * @param {Number} rowIndex
40943          * @param {Number} cellIndex
40944          * @param {Roo.EventObject} e
40945          */
40946          "cellcontextmenu" : true,
40947         /**
40948          * @event headercontextmenu
40949          * Fires when a header is right clicked
40950          * @param {Grid} this
40951          * @param {Number} columnIndex
40952          * @param {Roo.EventObject} e
40953          */
40954         "headercontextmenu" : true,
40955         /**
40956          * @event bodyscroll
40957          * Fires when the body element is scrolled
40958          * @param {Number} scrollLeft
40959          * @param {Number} scrollTop
40960          */
40961         "bodyscroll" : true,
40962         /**
40963          * @event columnresize
40964          * Fires when the user resizes a column
40965          * @param {Number} columnIndex
40966          * @param {Number} newSize
40967          */
40968         "columnresize" : true,
40969         /**
40970          * @event columnmove
40971          * Fires when the user moves a column
40972          * @param {Number} oldIndex
40973          * @param {Number} newIndex
40974          */
40975         "columnmove" : true,
40976         /**
40977          * @event startdrag
40978          * Fires when row(s) start being dragged
40979          * @param {Grid} this
40980          * @param {Roo.GridDD} dd The drag drop object
40981          * @param {event} e The raw browser event
40982          */
40983         "startdrag" : true,
40984         /**
40985          * @event enddrag
40986          * Fires when a drag operation is complete
40987          * @param {Grid} this
40988          * @param {Roo.GridDD} dd The drag drop object
40989          * @param {event} e The raw browser event
40990          */
40991         "enddrag" : true,
40992         /**
40993          * @event dragdrop
40994          * Fires when dragged row(s) are dropped on a valid DD target
40995          * @param {Grid} this
40996          * @param {Roo.GridDD} dd The drag drop object
40997          * @param {String} targetId The target drag drop object
40998          * @param {event} e The raw browser event
40999          */
41000         "dragdrop" : true,
41001         /**
41002          * @event dragover
41003          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
41004          * @param {Grid} this
41005          * @param {Roo.GridDD} dd The drag drop object
41006          * @param {String} targetId The target drag drop object
41007          * @param {event} e The raw browser event
41008          */
41009         "dragover" : true,
41010         /**
41011          * @event dragenter
41012          *  Fires when the dragged row(s) first cross another DD target while being dragged
41013          * @param {Grid} this
41014          * @param {Roo.GridDD} dd The drag drop object
41015          * @param {String} targetId The target drag drop object
41016          * @param {event} e The raw browser event
41017          */
41018         "dragenter" : true,
41019         /**
41020          * @event dragout
41021          * Fires when the dragged row(s) leave another DD target while being dragged
41022          * @param {Grid} this
41023          * @param {Roo.GridDD} dd The drag drop object
41024          * @param {String} targetId The target drag drop object
41025          * @param {event} e The raw browser event
41026          */
41027         "dragout" : true,
41028         /**
41029          * @event rowclass
41030          * Fires when a row is rendered, so you can change add a style to it.
41031          * @param {GridView} gridview   The grid view
41032          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
41033          */
41034         'rowclass' : true,
41035
41036         /**
41037          * @event render
41038          * Fires when the grid is rendered
41039          * @param {Grid} grid
41040          */
41041         'render' : true,
41042             /**
41043              * @event select
41044              * Fires when a date is selected
41045              * @param {DatePicker} this
41046              * @param {Date} date The selected date
41047              */
41048         'select': true,
41049         /**
41050              * @event monthchange
41051              * Fires when the displayed month changes 
41052              * @param {DatePicker} this
41053              * @param {Date} date The selected month
41054              */
41055         'monthchange': true,
41056         /**
41057              * @event evententer
41058              * Fires when mouse over an event
41059              * @param {Calendar} this
41060              * @param {event} Event
41061              */
41062         'evententer': true,
41063         /**
41064              * @event eventleave
41065              * Fires when the mouse leaves an
41066              * @param {Calendar} this
41067              * @param {event}
41068              */
41069         'eventleave': true,
41070         /**
41071              * @event eventclick
41072              * Fires when the mouse click an
41073              * @param {Calendar} this
41074              * @param {event}
41075              */
41076         'eventclick': true,
41077         /**
41078              * @event eventrender
41079              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
41080              * @param {Calendar} this
41081              * @param {data} data to be modified
41082              */
41083         'eventrender': true
41084         
41085     });
41086
41087     Roo.grid.Grid.superclass.constructor.call(this);
41088     this.on('render', function() {
41089         this.view.el.addClass('x-grid-cal'); 
41090         
41091         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
41092
41093     },this);
41094     
41095     if (!Roo.grid.Calendar.style) {
41096         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
41097             
41098             
41099             '.x-grid-cal .x-grid-col' :  {
41100                 height: 'auto !important',
41101                 'vertical-align': 'top'
41102             },
41103             '.x-grid-cal  .fc-event-hori' : {
41104                 height: '14px'
41105             }
41106              
41107             
41108         }, Roo.id());
41109     }
41110
41111     
41112     
41113 };
41114 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
41115     /**
41116      * @cfg {Store} eventStore The store that loads events.
41117      */
41118     eventStore : 25,
41119
41120      
41121     activeDate : false,
41122     startDay : 0,
41123     autoWidth : true,
41124     monitorWindowResize : false,
41125
41126     
41127     resizeColumns : function() {
41128         var col = (this.view.el.getWidth() / 7) - 3;
41129         // loop through cols, and setWidth
41130         for(var i =0 ; i < 7 ; i++){
41131             this.cm.setColumnWidth(i, col);
41132         }
41133     },
41134      setDate :function(date) {
41135         
41136         Roo.log('setDate?');
41137         
41138         this.resizeColumns();
41139         var vd = this.activeDate;
41140         this.activeDate = date;
41141 //        if(vd && this.el){
41142 //            var t = date.getTime();
41143 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
41144 //                Roo.log('using add remove');
41145 //                
41146 //                this.fireEvent('monthchange', this, date);
41147 //                
41148 //                this.cells.removeClass("fc-state-highlight");
41149 //                this.cells.each(function(c){
41150 //                   if(c.dateValue == t){
41151 //                       c.addClass("fc-state-highlight");
41152 //                       setTimeout(function(){
41153 //                            try{c.dom.firstChild.focus();}catch(e){}
41154 //                       }, 50);
41155 //                       return false;
41156 //                   }
41157 //                   return true;
41158 //                });
41159 //                return;
41160 //            }
41161 //        }
41162         
41163         var days = date.getDaysInMonth();
41164         
41165         var firstOfMonth = date.getFirstDateOfMonth();
41166         var startingPos = firstOfMonth.getDay()-this.startDay;
41167         
41168         if(startingPos < this.startDay){
41169             startingPos += 7;
41170         }
41171         
41172         var pm = date.add(Date.MONTH, -1);
41173         var prevStart = pm.getDaysInMonth()-startingPos;
41174 //        
41175         
41176         
41177         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
41178         
41179         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
41180         //this.cells.addClassOnOver('fc-state-hover');
41181         
41182         var cells = this.cells.elements;
41183         var textEls = this.textNodes;
41184         
41185         //Roo.each(cells, function(cell){
41186         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
41187         //});
41188         
41189         days += startingPos;
41190
41191         // convert everything to numbers so it's fast
41192         var day = 86400000;
41193         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
41194         //Roo.log(d);
41195         //Roo.log(pm);
41196         //Roo.log(prevStart);
41197         
41198         var today = new Date().clearTime().getTime();
41199         var sel = date.clearTime().getTime();
41200         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
41201         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
41202         var ddMatch = this.disabledDatesRE;
41203         var ddText = this.disabledDatesText;
41204         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
41205         var ddaysText = this.disabledDaysText;
41206         var format = this.format;
41207         
41208         var setCellClass = function(cal, cell){
41209             
41210             //Roo.log('set Cell Class');
41211             cell.title = "";
41212             var t = d.getTime();
41213             
41214             //Roo.log(d);
41215             
41216             
41217             cell.dateValue = t;
41218             if(t == today){
41219                 cell.className += " fc-today";
41220                 cell.className += " fc-state-highlight";
41221                 cell.title = cal.todayText;
41222             }
41223             if(t == sel){
41224                 // disable highlight in other month..
41225                 cell.className += " fc-state-highlight";
41226                 
41227             }
41228             // disabling
41229             if(t < min) {
41230                 //cell.className = " fc-state-disabled";
41231                 cell.title = cal.minText;
41232                 return;
41233             }
41234             if(t > max) {
41235                 //cell.className = " fc-state-disabled";
41236                 cell.title = cal.maxText;
41237                 return;
41238             }
41239             if(ddays){
41240                 if(ddays.indexOf(d.getDay()) != -1){
41241                     // cell.title = ddaysText;
41242                    // cell.className = " fc-state-disabled";
41243                 }
41244             }
41245             if(ddMatch && format){
41246                 var fvalue = d.dateFormat(format);
41247                 if(ddMatch.test(fvalue)){
41248                     cell.title = ddText.replace("%0", fvalue);
41249                    cell.className = " fc-state-disabled";
41250                 }
41251             }
41252             
41253             if (!cell.initialClassName) {
41254                 cell.initialClassName = cell.dom.className;
41255             }
41256             
41257             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
41258         };
41259
41260         var i = 0;
41261         
41262         for(; i < startingPos; i++) {
41263             cells[i].dayName =  (++prevStart);
41264             Roo.log(textEls[i]);
41265             d.setDate(d.getDate()+1);
41266             
41267             //cells[i].className = "fc-past fc-other-month";
41268             setCellClass(this, cells[i]);
41269         }
41270         
41271         var intDay = 0;
41272         
41273         for(; i < days; i++){
41274             intDay = i - startingPos + 1;
41275             cells[i].dayName =  (intDay);
41276             d.setDate(d.getDate()+1);
41277             
41278             cells[i].className = ''; // "x-date-active";
41279             setCellClass(this, cells[i]);
41280         }
41281         var extraDays = 0;
41282         
41283         for(; i < 42; i++) {
41284             //textEls[i].innerHTML = (++extraDays);
41285             
41286             d.setDate(d.getDate()+1);
41287             cells[i].dayName = (++extraDays);
41288             cells[i].className = "fc-future fc-other-month";
41289             setCellClass(this, cells[i]);
41290         }
41291         
41292         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
41293         
41294         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
41295         
41296         // this will cause all the cells to mis
41297         var rows= [];
41298         var i =0;
41299         for (var r = 0;r < 6;r++) {
41300             for (var c =0;c < 7;c++) {
41301                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
41302             }    
41303         }
41304         
41305         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
41306         for(i=0;i<cells.length;i++) {
41307             
41308             this.cells.elements[i].dayName = cells[i].dayName ;
41309             this.cells.elements[i].className = cells[i].className;
41310             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
41311             this.cells.elements[i].title = cells[i].title ;
41312             this.cells.elements[i].dateValue = cells[i].dateValue ;
41313         }
41314         
41315         
41316         
41317         
41318         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
41319         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
41320         
41321         ////if(totalRows != 6){
41322             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
41323            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
41324        // }
41325         
41326         this.fireEvent('monthchange', this, date);
41327         
41328         
41329     },
41330  /**
41331      * Returns the grid's SelectionModel.
41332      * @return {SelectionModel}
41333      */
41334     getSelectionModel : function(){
41335         if(!this.selModel){
41336             this.selModel = new Roo.grid.CellSelectionModel();
41337         }
41338         return this.selModel;
41339     },
41340
41341     load: function() {
41342         this.eventStore.load()
41343         
41344         
41345         
41346     },
41347     
41348     findCell : function(dt) {
41349         dt = dt.clearTime().getTime();
41350         var ret = false;
41351         this.cells.each(function(c){
41352             //Roo.log("check " +c.dateValue + '?=' + dt);
41353             if(c.dateValue == dt){
41354                 ret = c;
41355                 return false;
41356             }
41357             return true;
41358         });
41359         
41360         return ret;
41361     },
41362     
41363     findCells : function(rec) {
41364         var s = rec.data.start_dt.clone().clearTime().getTime();
41365        // Roo.log(s);
41366         var e= rec.data.end_dt.clone().clearTime().getTime();
41367        // Roo.log(e);
41368         var ret = [];
41369         this.cells.each(function(c){
41370              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
41371             
41372             if(c.dateValue > e){
41373                 return ;
41374             }
41375             if(c.dateValue < s){
41376                 return ;
41377             }
41378             ret.push(c);
41379         });
41380         
41381         return ret;    
41382     },
41383     
41384     findBestRow: function(cells)
41385     {
41386         var ret = 0;
41387         
41388         for (var i =0 ; i < cells.length;i++) {
41389             ret  = Math.max(cells[i].rows || 0,ret);
41390         }
41391         return ret;
41392         
41393     },
41394     
41395     
41396     addItem : function(rec)
41397     {
41398         // look for vertical location slot in
41399         var cells = this.findCells(rec);
41400         
41401         rec.row = this.findBestRow(cells);
41402         
41403         // work out the location.
41404         
41405         var crow = false;
41406         var rows = [];
41407         for(var i =0; i < cells.length; i++) {
41408             if (!crow) {
41409                 crow = {
41410                     start : cells[i],
41411                     end :  cells[i]
41412                 };
41413                 continue;
41414             }
41415             if (crow.start.getY() == cells[i].getY()) {
41416                 // on same row.
41417                 crow.end = cells[i];
41418                 continue;
41419             }
41420             // different row.
41421             rows.push(crow);
41422             crow = {
41423                 start: cells[i],
41424                 end : cells[i]
41425             };
41426             
41427         }
41428         
41429         rows.push(crow);
41430         rec.els = [];
41431         rec.rows = rows;
41432         rec.cells = cells;
41433         for (var i = 0; i < cells.length;i++) {
41434             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
41435             
41436         }
41437         
41438         
41439     },
41440     
41441     clearEvents: function() {
41442         
41443         if (!this.eventStore.getCount()) {
41444             return;
41445         }
41446         // reset number of rows in cells.
41447         Roo.each(this.cells.elements, function(c){
41448             c.rows = 0;
41449         });
41450         
41451         this.eventStore.each(function(e) {
41452             this.clearEvent(e);
41453         },this);
41454         
41455     },
41456     
41457     clearEvent : function(ev)
41458     {
41459         if (ev.els) {
41460             Roo.each(ev.els, function(el) {
41461                 el.un('mouseenter' ,this.onEventEnter, this);
41462                 el.un('mouseleave' ,this.onEventLeave, this);
41463                 el.remove();
41464             },this);
41465             ev.els = [];
41466         }
41467     },
41468     
41469     
41470     renderEvent : function(ev,ctr) {
41471         if (!ctr) {
41472              ctr = this.view.el.select('.fc-event-container',true).first();
41473         }
41474         
41475          
41476         this.clearEvent(ev);
41477             //code
41478        
41479         
41480         
41481         ev.els = [];
41482         var cells = ev.cells;
41483         var rows = ev.rows;
41484         this.fireEvent('eventrender', this, ev);
41485         
41486         for(var i =0; i < rows.length; i++) {
41487             
41488             cls = '';
41489             if (i == 0) {
41490                 cls += ' fc-event-start';
41491             }
41492             if ((i+1) == rows.length) {
41493                 cls += ' fc-event-end';
41494             }
41495             
41496             //Roo.log(ev.data);
41497             // how many rows should it span..
41498             var cg = this.eventTmpl.append(ctr,Roo.apply({
41499                 fccls : cls
41500                 
41501             }, ev.data) , true);
41502             
41503             
41504             cg.on('mouseenter' ,this.onEventEnter, this, ev);
41505             cg.on('mouseleave' ,this.onEventLeave, this, ev);
41506             cg.on('click', this.onEventClick, this, ev);
41507             
41508             ev.els.push(cg);
41509             
41510             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
41511             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
41512             //Roo.log(cg);
41513              
41514             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
41515             cg.setWidth(ebox.right - sbox.x -2);
41516         }
41517     },
41518     
41519     renderEvents: function()
41520     {   
41521         // first make sure there is enough space..
41522         
41523         if (!this.eventTmpl) {
41524             this.eventTmpl = new Roo.Template(
41525                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
41526                     '<div class="fc-event-inner">' +
41527                         '<span class="fc-event-time">{time}</span>' +
41528                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
41529                     '</div>' +
41530                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
41531                 '</div>'
41532             );
41533                 
41534         }
41535                
41536         
41537         
41538         this.cells.each(function(c) {
41539             //Roo.log(c.select('.fc-day-content div',true).first());
41540             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
41541         });
41542         
41543         var ctr = this.view.el.select('.fc-event-container',true).first();
41544         
41545         var cls;
41546         this.eventStore.each(function(ev){
41547             
41548             this.renderEvent(ev);
41549              
41550              
41551         }, this);
41552         this.view.layout();
41553         
41554     },
41555     
41556     onEventEnter: function (e, el,event,d) {
41557         this.fireEvent('evententer', this, el, event);
41558     },
41559     
41560     onEventLeave: function (e, el,event,d) {
41561         this.fireEvent('eventleave', this, el, event);
41562     },
41563     
41564     onEventClick: function (e, el,event,d) {
41565         this.fireEvent('eventclick', this, el, event);
41566     },
41567     
41568     onMonthChange: function () {
41569         this.store.load();
41570     },
41571     
41572     onLoad: function () {
41573         
41574         //Roo.log('calendar onload');
41575 //         
41576         if(this.eventStore.getCount() > 0){
41577             
41578            
41579             
41580             this.eventStore.each(function(d){
41581                 
41582                 
41583                 // FIXME..
41584                 var add =   d.data;
41585                 if (typeof(add.end_dt) == 'undefined')  {
41586                     Roo.log("Missing End time in calendar data: ");
41587                     Roo.log(d);
41588                     return;
41589                 }
41590                 if (typeof(add.start_dt) == 'undefined')  {
41591                     Roo.log("Missing Start time in calendar data: ");
41592                     Roo.log(d);
41593                     return;
41594                 }
41595                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
41596                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
41597                 add.id = add.id || d.id;
41598                 add.title = add.title || '??';
41599                 
41600                 this.addItem(d);
41601                 
41602              
41603             },this);
41604         }
41605         
41606         this.renderEvents();
41607     }
41608     
41609
41610 });
41611 /*
41612  grid : {
41613                 xtype: 'Grid',
41614                 xns: Roo.grid,
41615                 listeners : {
41616                     render : function ()
41617                     {
41618                         _this.grid = this;
41619                         
41620                         if (!this.view.el.hasClass('course-timesheet')) {
41621                             this.view.el.addClass('course-timesheet');
41622                         }
41623                         if (this.tsStyle) {
41624                             this.ds.load({});
41625                             return; 
41626                         }
41627                         Roo.log('width');
41628                         Roo.log(_this.grid.view.el.getWidth());
41629                         
41630                         
41631                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
41632                             '.course-timesheet .x-grid-row' : {
41633                                 height: '80px'
41634                             },
41635                             '.x-grid-row td' : {
41636                                 'vertical-align' : 0
41637                             },
41638                             '.course-edit-link' : {
41639                                 'color' : 'blue',
41640                                 'text-overflow' : 'ellipsis',
41641                                 'overflow' : 'hidden',
41642                                 'white-space' : 'nowrap',
41643                                 'cursor' : 'pointer'
41644                             },
41645                             '.sub-link' : {
41646                                 'color' : 'green'
41647                             },
41648                             '.de-act-sup-link' : {
41649                                 'color' : 'purple',
41650                                 'text-decoration' : 'line-through'
41651                             },
41652                             '.de-act-link' : {
41653                                 'color' : 'red',
41654                                 'text-decoration' : 'line-through'
41655                             },
41656                             '.course-timesheet .course-highlight' : {
41657                                 'border-top-style': 'dashed !important',
41658                                 'border-bottom-bottom': 'dashed !important'
41659                             },
41660                             '.course-timesheet .course-item' : {
41661                                 'font-family'   : 'tahoma, arial, helvetica',
41662                                 'font-size'     : '11px',
41663                                 'overflow'      : 'hidden',
41664                                 'padding-left'  : '10px',
41665                                 'padding-right' : '10px',
41666                                 'padding-top' : '10px' 
41667                             }
41668                             
41669                         }, Roo.id());
41670                                 this.ds.load({});
41671                     }
41672                 },
41673                 autoWidth : true,
41674                 monitorWindowResize : false,
41675                 cellrenderer : function(v,x,r)
41676                 {
41677                     return v;
41678                 },
41679                 sm : {
41680                     xtype: 'CellSelectionModel',
41681                     xns: Roo.grid
41682                 },
41683                 dataSource : {
41684                     xtype: 'Store',
41685                     xns: Roo.data,
41686                     listeners : {
41687                         beforeload : function (_self, options)
41688                         {
41689                             options.params = options.params || {};
41690                             options.params._month = _this.monthField.getValue();
41691                             options.params.limit = 9999;
41692                             options.params['sort'] = 'when_dt';    
41693                             options.params['dir'] = 'ASC';    
41694                             this.proxy.loadResponse = this.loadResponse;
41695                             Roo.log("load?");
41696                             //this.addColumns();
41697                         },
41698                         load : function (_self, records, options)
41699                         {
41700                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
41701                                 // if you click on the translation.. you can edit it...
41702                                 var el = Roo.get(this);
41703                                 var id = el.dom.getAttribute('data-id');
41704                                 var d = el.dom.getAttribute('data-date');
41705                                 var t = el.dom.getAttribute('data-time');
41706                                 //var id = this.child('span').dom.textContent;
41707                                 
41708                                 //Roo.log(this);
41709                                 Pman.Dialog.CourseCalendar.show({
41710                                     id : id,
41711                                     when_d : d,
41712                                     when_t : t,
41713                                     productitem_active : id ? 1 : 0
41714                                 }, function() {
41715                                     _this.grid.ds.load({});
41716                                 });
41717                            
41718                            });
41719                            
41720                            _this.panel.fireEvent('resize', [ '', '' ]);
41721                         }
41722                     },
41723                     loadResponse : function(o, success, response){
41724                             // this is overridden on before load..
41725                             
41726                             Roo.log("our code?");       
41727                             //Roo.log(success);
41728                             //Roo.log(response)
41729                             delete this.activeRequest;
41730                             if(!success){
41731                                 this.fireEvent("loadexception", this, o, response);
41732                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41733                                 return;
41734                             }
41735                             var result;
41736                             try {
41737                                 result = o.reader.read(response);
41738                             }catch(e){
41739                                 Roo.log("load exception?");
41740                                 this.fireEvent("loadexception", this, o, response, e);
41741                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41742                                 return;
41743                             }
41744                             Roo.log("ready...");        
41745                             // loop through result.records;
41746                             // and set this.tdate[date] = [] << array of records..
41747                             _this.tdata  = {};
41748                             Roo.each(result.records, function(r){
41749                                 //Roo.log(r.data);
41750                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
41751                                     _this.tdata[r.data.when_dt.format('j')] = [];
41752                                 }
41753                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
41754                             });
41755                             
41756                             //Roo.log(_this.tdata);
41757                             
41758                             result.records = [];
41759                             result.totalRecords = 6;
41760                     
41761                             // let's generate some duumy records for the rows.
41762                             //var st = _this.dateField.getValue();
41763                             
41764                             // work out monday..
41765                             //st = st.add(Date.DAY, -1 * st.format('w'));
41766                             
41767                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41768                             
41769                             var firstOfMonth = date.getFirstDayOfMonth();
41770                             var days = date.getDaysInMonth();
41771                             var d = 1;
41772                             var firstAdded = false;
41773                             for (var i = 0; i < result.totalRecords ; i++) {
41774                                 //var d= st.add(Date.DAY, i);
41775                                 var row = {};
41776                                 var added = 0;
41777                                 for(var w = 0 ; w < 7 ; w++){
41778                                     if(!firstAdded && firstOfMonth != w){
41779                                         continue;
41780                                     }
41781                                     if(d > days){
41782                                         continue;
41783                                     }
41784                                     firstAdded = true;
41785                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
41786                                     row['weekday'+w] = String.format(
41787                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
41788                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
41789                                                     d,
41790                                                     date.format('Y-m-')+dd
41791                                                 );
41792                                     added++;
41793                                     if(typeof(_this.tdata[d]) != 'undefined'){
41794                                         Roo.each(_this.tdata[d], function(r){
41795                                             var is_sub = '';
41796                                             var deactive = '';
41797                                             var id = r.id;
41798                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
41799                                             if(r.parent_id*1>0){
41800                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
41801                                                 id = r.parent_id;
41802                                             }
41803                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
41804                                                 deactive = 'de-act-link';
41805                                             }
41806                                             
41807                                             row['weekday'+w] += String.format(
41808                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
41809                                                     id, //0
41810                                                     r.product_id_name, //1
41811                                                     r.when_dt.format('h:ia'), //2
41812                                                     is_sub, //3
41813                                                     deactive, //4
41814                                                     desc // 5
41815                                             );
41816                                         });
41817                                     }
41818                                     d++;
41819                                 }
41820                                 
41821                                 // only do this if something added..
41822                                 if(added > 0){ 
41823                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
41824                                 }
41825                                 
41826                                 
41827                                 // push it twice. (second one with an hour..
41828                                 
41829                             }
41830                             //Roo.log(result);
41831                             this.fireEvent("load", this, o, o.request.arg);
41832                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
41833                         },
41834                     sortInfo : {field: 'when_dt', direction : 'ASC' },
41835                     proxy : {
41836                         xtype: 'HttpProxy',
41837                         xns: Roo.data,
41838                         method : 'GET',
41839                         url : baseURL + '/Roo/Shop_course.php'
41840                     },
41841                     reader : {
41842                         xtype: 'JsonReader',
41843                         xns: Roo.data,
41844                         id : 'id',
41845                         fields : [
41846                             {
41847                                 'name': 'id',
41848                                 'type': 'int'
41849                             },
41850                             {
41851                                 'name': 'when_dt',
41852                                 'type': 'string'
41853                             },
41854                             {
41855                                 'name': 'end_dt',
41856                                 'type': 'string'
41857                             },
41858                             {
41859                                 'name': 'parent_id',
41860                                 'type': 'int'
41861                             },
41862                             {
41863                                 'name': 'product_id',
41864                                 'type': 'int'
41865                             },
41866                             {
41867                                 'name': 'productitem_id',
41868                                 'type': 'int'
41869                             },
41870                             {
41871                                 'name': 'guid',
41872                                 'type': 'int'
41873                             }
41874                         ]
41875                     }
41876                 },
41877                 toolbar : {
41878                     xtype: 'Toolbar',
41879                     xns: Roo,
41880                     items : [
41881                         {
41882                             xtype: 'Button',
41883                             xns: Roo.Toolbar,
41884                             listeners : {
41885                                 click : function (_self, e)
41886                                 {
41887                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41888                                     sd.setMonth(sd.getMonth()-1);
41889                                     _this.monthField.setValue(sd.format('Y-m-d'));
41890                                     _this.grid.ds.load({});
41891                                 }
41892                             },
41893                             text : "Back"
41894                         },
41895                         {
41896                             xtype: 'Separator',
41897                             xns: Roo.Toolbar
41898                         },
41899                         {
41900                             xtype: 'MonthField',
41901                             xns: Roo.form,
41902                             listeners : {
41903                                 render : function (_self)
41904                                 {
41905                                     _this.monthField = _self;
41906                                    // _this.monthField.set  today
41907                                 },
41908                                 select : function (combo, date)
41909                                 {
41910                                     _this.grid.ds.load({});
41911                                 }
41912                             },
41913                             value : (function() { return new Date(); })()
41914                         },
41915                         {
41916                             xtype: 'Separator',
41917                             xns: Roo.Toolbar
41918                         },
41919                         {
41920                             xtype: 'TextItem',
41921                             xns: Roo.Toolbar,
41922                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
41923                         },
41924                         {
41925                             xtype: 'Fill',
41926                             xns: Roo.Toolbar
41927                         },
41928                         {
41929                             xtype: 'Button',
41930                             xns: Roo.Toolbar,
41931                             listeners : {
41932                                 click : function (_self, e)
41933                                 {
41934                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41935                                     sd.setMonth(sd.getMonth()+1);
41936                                     _this.monthField.setValue(sd.format('Y-m-d'));
41937                                     _this.grid.ds.load({});
41938                                 }
41939                             },
41940                             text : "Next"
41941                         }
41942                     ]
41943                 },
41944                  
41945             }
41946         };
41947         
41948         *//*
41949  * Based on:
41950  * Ext JS Library 1.1.1
41951  * Copyright(c) 2006-2007, Ext JS, LLC.
41952  *
41953  * Originally Released Under LGPL - original licence link has changed is not relivant.
41954  *
41955  * Fork - LGPL
41956  * <script type="text/javascript">
41957  */
41958  
41959 /**
41960  * @class Roo.LoadMask
41961  * A simple utility class for generically masking elements while loading data.  If the element being masked has
41962  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
41963  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
41964  * element's UpdateManager load indicator and will be destroyed after the initial load.
41965  * @constructor
41966  * Create a new LoadMask
41967  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
41968  * @param {Object} config The config object
41969  */
41970 Roo.LoadMask = function(el, config){
41971     this.el = Roo.get(el);
41972     Roo.apply(this, config);
41973     if(this.store){
41974         this.store.on('beforeload', this.onBeforeLoad, this);
41975         this.store.on('load', this.onLoad, this);
41976         this.store.on('loadexception', this.onLoadException, this);
41977         this.removeMask = false;
41978     }else{
41979         var um = this.el.getUpdateManager();
41980         um.showLoadIndicator = false; // disable the default indicator
41981         um.on('beforeupdate', this.onBeforeLoad, this);
41982         um.on('update', this.onLoad, this);
41983         um.on('failure', this.onLoad, this);
41984         this.removeMask = true;
41985     }
41986 };
41987
41988 Roo.LoadMask.prototype = {
41989     /**
41990      * @cfg {Boolean} removeMask
41991      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
41992      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
41993      */
41994     /**
41995      * @cfg {String} msg
41996      * The text to display in a centered loading message box (defaults to 'Loading...')
41997      */
41998     msg : 'Loading...',
41999     /**
42000      * @cfg {String} msgCls
42001      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
42002      */
42003     msgCls : 'x-mask-loading',
42004
42005     /**
42006      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
42007      * @type Boolean
42008      */
42009     disabled: false,
42010
42011     /**
42012      * Disables the mask to prevent it from being displayed
42013      */
42014     disable : function(){
42015        this.disabled = true;
42016     },
42017
42018     /**
42019      * Enables the mask so that it can be displayed
42020      */
42021     enable : function(){
42022         this.disabled = false;
42023     },
42024     
42025     onLoadException : function()
42026     {
42027         Roo.log(arguments);
42028         
42029         if (typeof(arguments[3]) != 'undefined') {
42030             Roo.MessageBox.alert("Error loading",arguments[3]);
42031         } 
42032         /*
42033         try {
42034             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
42035                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
42036             }   
42037         } catch(e) {
42038             
42039         }
42040         */
42041     
42042         
42043         
42044         this.el.unmask(this.removeMask);
42045     },
42046     // private
42047     onLoad : function()
42048     {
42049         this.el.unmask(this.removeMask);
42050     },
42051
42052     // private
42053     onBeforeLoad : function(){
42054         if(!this.disabled){
42055             this.el.mask(this.msg, this.msgCls);
42056         }
42057     },
42058
42059     // private
42060     destroy : function(){
42061         if(this.store){
42062             this.store.un('beforeload', this.onBeforeLoad, this);
42063             this.store.un('load', this.onLoad, this);
42064             this.store.un('loadexception', this.onLoadException, this);
42065         }else{
42066             var um = this.el.getUpdateManager();
42067             um.un('beforeupdate', this.onBeforeLoad, this);
42068             um.un('update', this.onLoad, this);
42069             um.un('failure', this.onLoad, this);
42070         }
42071     }
42072 };/*
42073  * Based on:
42074  * Ext JS Library 1.1.1
42075  * Copyright(c) 2006-2007, Ext JS, LLC.
42076  *
42077  * Originally Released Under LGPL - original licence link has changed is not relivant.
42078  *
42079  * Fork - LGPL
42080  * <script type="text/javascript">
42081  */
42082
42083
42084 /**
42085  * @class Roo.XTemplate
42086  * @extends Roo.Template
42087  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
42088 <pre><code>
42089 var t = new Roo.XTemplate(
42090         '&lt;select name="{name}"&gt;',
42091                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
42092         '&lt;/select&gt;'
42093 );
42094  
42095 // then append, applying the master template values
42096  </code></pre>
42097  *
42098  * Supported features:
42099  *
42100  *  Tags:
42101
42102 <pre><code>
42103       {a_variable} - output encoded.
42104       {a_variable.format:("Y-m-d")} - call a method on the variable
42105       {a_variable:raw} - unencoded output
42106       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
42107       {a_variable:this.method_on_template(...)} - call a method on the template object.
42108  
42109 </code></pre>
42110  *  The tpl tag:
42111 <pre><code>
42112         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
42113         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
42114         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
42115         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
42116   
42117         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
42118         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
42119 </code></pre>
42120  *      
42121  */
42122 Roo.XTemplate = function()
42123 {
42124     Roo.XTemplate.superclass.constructor.apply(this, arguments);
42125     if (this.html) {
42126         this.compile();
42127     }
42128 };
42129
42130
42131 Roo.extend(Roo.XTemplate, Roo.Template, {
42132
42133     /**
42134      * The various sub templates
42135      */
42136     tpls : false,
42137     /**
42138      *
42139      * basic tag replacing syntax
42140      * WORD:WORD()
42141      *
42142      * // you can fake an object call by doing this
42143      *  x.t:(test,tesT) 
42144      * 
42145      */
42146     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
42147
42148     /**
42149      * compile the template
42150      *
42151      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
42152      *
42153      */
42154     compile: function()
42155     {
42156         var s = this.html;
42157      
42158         s = ['<tpl>', s, '</tpl>'].join('');
42159     
42160         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
42161             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
42162             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
42163             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
42164             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
42165             m,
42166             id     = 0,
42167             tpls   = [];
42168     
42169         while(true == !!(m = s.match(re))){
42170             var forMatch   = m[0].match(nameRe),
42171                 ifMatch   = m[0].match(ifRe),
42172                 execMatch   = m[0].match(execRe),
42173                 namedMatch   = m[0].match(namedRe),
42174                 
42175                 exp  = null, 
42176                 fn   = null,
42177                 exec = null,
42178                 name = forMatch && forMatch[1] ? forMatch[1] : '';
42179                 
42180             if (ifMatch) {
42181                 // if - puts fn into test..
42182                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
42183                 if(exp){
42184                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
42185                 }
42186             }
42187             
42188             if (execMatch) {
42189                 // exec - calls a function... returns empty if true is  returned.
42190                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
42191                 if(exp){
42192                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
42193                 }
42194             }
42195             
42196             
42197             if (name) {
42198                 // for = 
42199                 switch(name){
42200                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
42201                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
42202                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
42203                 }
42204             }
42205             var uid = namedMatch ? namedMatch[1] : id;
42206             
42207             
42208             tpls.push({
42209                 id:     namedMatch ? namedMatch[1] : id,
42210                 target: name,
42211                 exec:   exec,
42212                 test:   fn,
42213                 body:   m[1] || ''
42214             });
42215             if (namedMatch) {
42216                 s = s.replace(m[0], '');
42217             } else { 
42218                 s = s.replace(m[0], '{xtpl'+ id + '}');
42219             }
42220             ++id;
42221         }
42222         this.tpls = [];
42223         for(var i = tpls.length-1; i >= 0; --i){
42224             this.compileTpl(tpls[i]);
42225             this.tpls[tpls[i].id] = tpls[i];
42226         }
42227         this.master = tpls[tpls.length-1];
42228         return this;
42229     },
42230     /**
42231      * same as applyTemplate, except it's done to one of the subTemplates
42232      * when using named templates, you can do:
42233      *
42234      * var str = pl.applySubTemplate('your-name', values);
42235      *
42236      * 
42237      * @param {Number} id of the template
42238      * @param {Object} values to apply to template
42239      * @param {Object} parent (normaly the instance of this object)
42240      */
42241     applySubTemplate : function(id, values, parent)
42242     {
42243         
42244         
42245         var t = this.tpls[id];
42246         
42247         
42248         try { 
42249             if(t.test && !t.test.call(this, values, parent)){
42250                 return '';
42251             }
42252         } catch(e) {
42253             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
42254             Roo.log(e.toString());
42255             Roo.log(t.test);
42256             return ''
42257         }
42258         try { 
42259             
42260             if(t.exec && t.exec.call(this, values, parent)){
42261                 return '';
42262             }
42263         } catch(e) {
42264             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
42265             Roo.log(e.toString());
42266             Roo.log(t.exec);
42267             return ''
42268         }
42269         try {
42270             var vs = t.target ? t.target.call(this, values, parent) : values;
42271             parent = t.target ? values : parent;
42272             if(t.target && vs instanceof Array){
42273                 var buf = [];
42274                 for(var i = 0, len = vs.length; i < len; i++){
42275                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
42276                 }
42277                 return buf.join('');
42278             }
42279             return t.compiled.call(this, vs, parent);
42280         } catch (e) {
42281             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
42282             Roo.log(e.toString());
42283             Roo.log(t.compiled);
42284             return '';
42285         }
42286     },
42287
42288     compileTpl : function(tpl)
42289     {
42290         var fm = Roo.util.Format;
42291         var useF = this.disableFormats !== true;
42292         var sep = Roo.isGecko ? "+" : ",";
42293         var undef = function(str) {
42294             Roo.log("Property not found :"  + str);
42295             return '';
42296         };
42297         
42298         var fn = function(m, name, format, args)
42299         {
42300             //Roo.log(arguments);
42301             args = args ? args.replace(/\\'/g,"'") : args;
42302             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
42303             if (typeof(format) == 'undefined') {
42304                 format= 'htmlEncode';
42305             }
42306             if (format == 'raw' ) {
42307                 format = false;
42308             }
42309             
42310             if(name.substr(0, 4) == 'xtpl'){
42311                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
42312             }
42313             
42314             // build an array of options to determine if value is undefined..
42315             
42316             // basically get 'xxxx.yyyy' then do
42317             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
42318             //    (function () { Roo.log("Property not found"); return ''; })() :
42319             //    ......
42320             
42321             var udef_ar = [];
42322             var lookfor = '';
42323             Roo.each(name.split('.'), function(st) {
42324                 lookfor += (lookfor.length ? '.': '') + st;
42325                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
42326             });
42327             
42328             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
42329             
42330             
42331             if(format && useF){
42332                 
42333                 args = args ? ',' + args : "";
42334                  
42335                 if(format.substr(0, 5) != "this."){
42336                     format = "fm." + format + '(';
42337                 }else{
42338                     format = 'this.call("'+ format.substr(5) + '", ';
42339                     args = ", values";
42340                 }
42341                 
42342                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
42343             }
42344              
42345             if (args.length) {
42346                 // called with xxyx.yuu:(test,test)
42347                 // change to ()
42348                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
42349             }
42350             // raw.. - :raw modifier..
42351             return "'"+ sep + udef_st  + name + ")"+sep+"'";
42352             
42353         };
42354         var body;
42355         // branched to use + in gecko and [].join() in others
42356         if(Roo.isGecko){
42357             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
42358                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
42359                     "';};};";
42360         }else{
42361             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
42362             body.push(tpl.body.replace(/(\r\n|\n)/g,
42363                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
42364             body.push("'].join('');};};");
42365             body = body.join('');
42366         }
42367         
42368         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
42369        
42370         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
42371         eval(body);
42372         
42373         return this;
42374     },
42375
42376     applyTemplate : function(values){
42377         return this.master.compiled.call(this, values, {});
42378         //var s = this.subs;
42379     },
42380
42381     apply : function(){
42382         return this.applyTemplate.apply(this, arguments);
42383     }
42384
42385  });
42386
42387 Roo.XTemplate.from = function(el){
42388     el = Roo.getDom(el);
42389     return new Roo.XTemplate(el.value || el.innerHTML);
42390 };