roojs-ui.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13
14 /*
15  * These classes are derivatives of the similarly named classes in the YUI Library.
16  * The original license:
17  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18  * Code licensed under the BSD License:
19  * http://developer.yahoo.net/yui/license.txt
20  */
21
22 (function() {
23
24 var Event=Roo.EventManager;
25 var Dom=Roo.lib.Dom;
26
27 /**
28  * @class Roo.dd.DragDrop
29  * @extends Roo.util.Observable
30  * Defines the interface and base operation of items that that can be
31  * dragged or can be drop targets.  It was designed to be extended, overriding
32  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
33  * Up to three html elements can be associated with a DragDrop instance:
34  * <ul>
35  * <li>linked element: the element that is passed into the constructor.
36  * This is the element which defines the boundaries for interaction with
37  * other DragDrop objects.</li>
38  * <li>handle element(s): The drag operation only occurs if the element that
39  * was clicked matches a handle element.  By default this is the linked
40  * element, but there are times that you will want only a portion of the
41  * linked element to initiate the drag operation, and the setHandleElId()
42  * method provides a way to define this.</li>
43  * <li>drag element: this represents the element that would be moved along
44  * with the cursor during a drag operation.  By default, this is the linked
45  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
46  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
47  * </li>
48  * </ul>
49  * This class should not be instantiated until the onload event to ensure that
50  * the associated elements are available.
51  * The following would define a DragDrop obj that would interact with any
52  * other DragDrop obj in the "group1" group:
53  * <pre>
54  *  dd = new Roo.dd.DragDrop("div1", "group1");
55  * </pre>
56  * Since none of the event handlers have been implemented, nothing would
57  * actually happen if you were to run the code above.  Normally you would
58  * override this class or one of the default implementations, but you can
59  * also override the methods you want on an instance of the class...
60  * <pre>
61  *  dd.onDragDrop = function(e, id) {
62  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
63  *  }
64  * </pre>
65  * @constructor
66  * @param {String} id of the element that is linked to this instance
67  * @param {String} sGroup the group of related DragDrop objects
68  * @param {object} config an object containing configurable attributes
69  *                Valid properties for DragDrop:
70  *                    padding, isTarget, maintainOffset, primaryButtonOnly
71  */
72 Roo.dd.DragDrop = function(id, sGroup, config) {
73     if (id) {
74         this.init(id, sGroup, config);
75     }
76     
77 };
78
79 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
80
81     /**
82      * The id of the element associated with this object.  This is what we
83      * refer to as the "linked element" because the size and position of
84      * this element is used to determine when the drag and drop objects have
85      * interacted.
86      * @property id
87      * @type String
88      */
89     id: null,
90
91     /**
92      * Configuration attributes passed into the constructor
93      * @property config
94      * @type object
95      */
96     config: null,
97
98     /**
99      * The id of the element that will be dragged.  By default this is same
100      * as the linked element , but could be changed to another element. Ex:
101      * Roo.dd.DDProxy
102      * @property dragElId
103      * @type String
104      * @private
105      */
106     dragElId: null,
107
108     /**
109      * the id of the element that initiates the drag operation.  By default
110      * this is the linked element, but could be changed to be a child of this
111      * element.  This lets us do things like only starting the drag when the
112      * header element within the linked html element is clicked.
113      * @property handleElId
114      * @type String
115      * @private
116      */
117     handleElId: null,
118
119     /**
120      * An associative array of HTML tags that will be ignored if clicked.
121      * @property invalidHandleTypes
122      * @type {string: string}
123      */
124     invalidHandleTypes: null,
125
126     /**
127      * An associative array of ids for elements that will be ignored if clicked
128      * @property invalidHandleIds
129      * @type {string: string}
130      */
131     invalidHandleIds: null,
132
133     /**
134      * An indexted array of css class names for elements that will be ignored
135      * if clicked.
136      * @property invalidHandleClasses
137      * @type string[]
138      */
139     invalidHandleClasses: null,
140
141     /**
142      * The linked element's absolute X position at the time the drag was
143      * started
144      * @property startPageX
145      * @type int
146      * @private
147      */
148     startPageX: 0,
149
150     /**
151      * The linked element's absolute X position at the time the drag was
152      * started
153      * @property startPageY
154      * @type int
155      * @private
156      */
157     startPageY: 0,
158
159     /**
160      * The group defines a logical collection of DragDrop objects that are
161      * related.  Instances only get events when interacting with other
162      * DragDrop object in the same group.  This lets us define multiple
163      * groups using a single DragDrop subclass if we want.
164      * @property groups
165      * @type {string: string}
166      */
167     groups: null,
168
169     /**
170      * Individual drag/drop instances can be locked.  This will prevent
171      * onmousedown start drag.
172      * @property locked
173      * @type boolean
174      * @private
175      */
176     locked: false,
177
178     /**
179      * Lock this instance
180      * @method lock
181      */
182     lock: function() { this.locked = true; },
183
184     /**
185      * Unlock this instace
186      * @method unlock
187      */
188     unlock: function() { this.locked = false; },
189
190     /**
191      * By default, all insances can be a drop target.  This can be disabled by
192      * setting isTarget to false.
193      * @method isTarget
194      * @type boolean
195      */
196     isTarget: true,
197
198     /**
199      * The padding configured for this drag and drop object for calculating
200      * the drop zone intersection with this object.
201      * @method padding
202      * @type int[]
203      */
204     padding: null,
205
206     /**
207      * Cached reference to the linked element
208      * @property _domRef
209      * @private
210      */
211     _domRef: null,
212
213     /**
214      * Internal typeof flag
215      * @property __ygDragDrop
216      * @private
217      */
218     __ygDragDrop: true,
219
220     /**
221      * Set to true when horizontal contraints are applied
222      * @property constrainX
223      * @type boolean
224      * @private
225      */
226     constrainX: false,
227
228     /**
229      * Set to true when vertical contraints are applied
230      * @property constrainY
231      * @type boolean
232      * @private
233      */
234     constrainY: false,
235
236     /**
237      * The left constraint
238      * @property minX
239      * @type int
240      * @private
241      */
242     minX: 0,
243
244     /**
245      * The right constraint
246      * @property maxX
247      * @type int
248      * @private
249      */
250     maxX: 0,
251
252     /**
253      * The up constraint
254      * @property minY
255      * @type int
256      * @type int
257      * @private
258      */
259     minY: 0,
260
261     /**
262      * The down constraint
263      * @property maxY
264      * @type int
265      * @private
266      */
267     maxY: 0,
268
269     /**
270      * Maintain offsets when we resetconstraints.  Set to true when you want
271      * the position of the element relative to its parent to stay the same
272      * when the page changes
273      *
274      * @property maintainOffset
275      * @type boolean
276      */
277     maintainOffset: false,
278
279     /**
280      * Array of pixel locations the element will snap to if we specified a
281      * horizontal graduation/interval.  This array is generated automatically
282      * when you define a tick interval.
283      * @property xTicks
284      * @type int[]
285      */
286     xTicks: null,
287
288     /**
289      * Array of pixel locations the element will snap to if we specified a
290      * vertical graduation/interval.  This array is generated automatically
291      * when you define a tick interval.
292      * @property yTicks
293      * @type int[]
294      */
295     yTicks: null,
296
297     /**
298      * By default the drag and drop instance will only respond to the primary
299      * button click (left button for a right-handed mouse).  Set to true to
300      * allow drag and drop to start with any mouse click that is propogated
301      * by the browser
302      * @property primaryButtonOnly
303      * @type boolean
304      */
305     primaryButtonOnly: true,
306
307     /**
308      * The availabe property is false until the linked dom element is accessible.
309      * @property available
310      * @type boolean
311      */
312     available: false,
313
314     /**
315      * By default, drags can only be initiated if the mousedown occurs in the
316      * region the linked element is.  This is done in part to work around a
317      * bug in some browsers that mis-report the mousedown if the previous
318      * mouseup happened outside of the window.  This property is set to true
319      * if outer handles are defined.
320      *
321      * @property hasOuterHandles
322      * @type boolean
323      * @default false
324      */
325     hasOuterHandles: false,
326
327     /**
328      * Code that executes immediately before the startDrag event
329      * @method b4StartDrag
330      * @private
331      */
332     b4StartDrag: function(x, y) { },
333
334     /**
335      * Abstract method called after a drag/drop object is clicked
336      * and the drag or mousedown time thresholds have beeen met.
337      * @method startDrag
338      * @param {int} X click location
339      * @param {int} Y click location
340      */
341     startDrag: function(x, y) { /* override this */ },
342
343     /**
344      * Code that executes immediately before the onDrag event
345      * @method b4Drag
346      * @private
347      */
348     b4Drag: function(e) { },
349
350     /**
351      * Abstract method called during the onMouseMove event while dragging an
352      * object.
353      * @method onDrag
354      * @param {Event} e the mousemove event
355      */
356     onDrag: function(e) { /* override this */ },
357
358     /**
359      * Abstract method called when this element fist begins hovering over
360      * another DragDrop obj
361      * @method onDragEnter
362      * @param {Event} e the mousemove event
363      * @param {String|DragDrop[]} id In POINT mode, the element
364      * id this is hovering over.  In INTERSECT mode, an array of one or more
365      * dragdrop items being hovered over.
366      */
367     onDragEnter: function(e, id) { /* override this */ },
368
369     /**
370      * Code that executes immediately before the onDragOver event
371      * @method b4DragOver
372      * @private
373      */
374     b4DragOver: function(e) { },
375
376     /**
377      * Abstract method called when this element is hovering over another
378      * DragDrop obj
379      * @method onDragOver
380      * @param {Event} e the mousemove event
381      * @param {String|DragDrop[]} id In POINT mode, the element
382      * id this is hovering over.  In INTERSECT mode, an array of dd items
383      * being hovered over.
384      */
385     onDragOver: function(e, id) { /* override this */ },
386
387     /**
388      * Code that executes immediately before the onDragOut event
389      * @method b4DragOut
390      * @private
391      */
392     b4DragOut: function(e) { },
393
394     /**
395      * Abstract method called when we are no longer hovering over an element
396      * @method onDragOut
397      * @param {Event} e the mousemove event
398      * @param {String|DragDrop[]} id In POINT mode, the element
399      * id this was hovering over.  In INTERSECT mode, an array of dd items
400      * that the mouse is no longer over.
401      */
402     onDragOut: function(e, id) { /* override this */ },
403
404     /**
405      * Code that executes immediately before the onDragDrop event
406      * @method b4DragDrop
407      * @private
408      */
409     b4DragDrop: function(e) { },
410
411     /**
412      * Abstract method called when this item is dropped on another DragDrop
413      * obj
414      * @method onDragDrop
415      * @param {Event} e the mouseup event
416      * @param {String|DragDrop[]} id In POINT mode, the element
417      * id this was dropped on.  In INTERSECT mode, an array of dd items this
418      * was dropped on.
419      */
420     onDragDrop: function(e, id) { /* override this */ },
421
422     /**
423      * Abstract method called when this item is dropped on an area with no
424      * drop target
425      * @method onInvalidDrop
426      * @param {Event} e the mouseup event
427      */
428     onInvalidDrop: function(e) { /* override this */ },
429
430     /**
431      * Code that executes immediately before the endDrag event
432      * @method b4EndDrag
433      * @private
434      */
435     b4EndDrag: function(e) { },
436
437     /**
438      * Fired when we are done dragging the object
439      * @method endDrag
440      * @param {Event} e the mouseup event
441      */
442     endDrag: function(e) { /* override this */ },
443
444     /**
445      * Code executed immediately before the onMouseDown event
446      * @method b4MouseDown
447      * @param {Event} e the mousedown event
448      * @private
449      */
450     b4MouseDown: function(e) {  },
451
452     /**
453      * Event handler that fires when a drag/drop obj gets a mousedown
454      * @method onMouseDown
455      * @param {Event} e the mousedown event
456      */
457     onMouseDown: function(e) { /* override this */ },
458
459     /**
460      * Event handler that fires when a drag/drop obj gets a mouseup
461      * @method onMouseUp
462      * @param {Event} e the mouseup event
463      */
464     onMouseUp: function(e) { /* override this */ },
465
466     /**
467      * Override the onAvailable method to do what is needed after the initial
468      * position was determined.
469      * @method onAvailable
470      */
471     onAvailable: function () {
472     },
473
474     /*
475      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
476      * @type Object
477      */
478     defaultPadding : {left:0, right:0, top:0, bottom:0},
479
480     /*
481      * Initializes the drag drop object's constraints to restrict movement to a certain element.
482  *
483  * Usage:
484  <pre><code>
485  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
486                 { dragElId: "existingProxyDiv" });
487  dd.startDrag = function(){
488      this.constrainTo("parent-id");
489  };
490  </code></pre>
491  * Or you can initalize it using the {@link Roo.Element} object:
492  <pre><code>
493  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
494      startDrag : function(){
495          this.constrainTo("parent-id");
496      }
497  });
498  </code></pre>
499      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
500      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
501      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
502      * an object containing the sides to pad. For example: {right:10, bottom:10}
503      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
504      */
505     constrainTo : function(constrainTo, pad, inContent){
506         if(typeof pad == "number"){
507             pad = {left: pad, right:pad, top:pad, bottom:pad};
508         }
509         pad = pad || this.defaultPadding;
510         var b = Roo.get(this.getEl()).getBox();
511         var ce = Roo.get(constrainTo);
512         var s = ce.getScroll();
513         var c, cd = ce.dom;
514         if(cd == document.body){
515             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
516         }else{
517             xy = ce.getXY();
518             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
519         }
520
521
522         var topSpace = b.y - c.y;
523         var leftSpace = b.x - c.x;
524
525         this.resetConstraints();
526         this.setXConstraint(leftSpace - (pad.left||0), // left
527                 c.width - leftSpace - b.width - (pad.right||0) //right
528         );
529         this.setYConstraint(topSpace - (pad.top||0), //top
530                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
531         );
532     },
533
534     /**
535      * Returns a reference to the linked element
536      * @method getEl
537      * @return {HTMLElement} the html element
538      */
539     getEl: function() {
540         if (!this._domRef) {
541             this._domRef = Roo.getDom(this.id);
542         }
543
544         return this._domRef;
545     },
546
547     /**
548      * Returns a reference to the actual element to drag.  By default this is
549      * the same as the html element, but it can be assigned to another
550      * element. An example of this can be found in Roo.dd.DDProxy
551      * @method getDragEl
552      * @return {HTMLElement} the html element
553      */
554     getDragEl: function() {
555         return Roo.getDom(this.dragElId);
556     },
557
558     /**
559      * Sets up the DragDrop object.  Must be called in the constructor of any
560      * Roo.dd.DragDrop subclass
561      * @method init
562      * @param id the id of the linked element
563      * @param {String} sGroup the group of related items
564      * @param {object} config configuration attributes
565      */
566     init: function(id, sGroup, config) {
567         this.initTarget(id, sGroup, config);
568         if (!Roo.isTouch) {
569             Event.on(this.id, "mousedown", this.handleMouseDown, this);
570         }
571         Event.on(this.id, "touchstart", this.handleMouseDown, this);
572         // Event.on(this.id, "selectstart", Event.preventDefault);
573     },
574
575     /**
576      * Initializes Targeting functionality only... the object does not
577      * get a mousedown handler.
578      * @method initTarget
579      * @param id the id of the linked element
580      * @param {String} sGroup the group of related items
581      * @param {object} config configuration attributes
582      */
583     initTarget: function(id, sGroup, config) {
584
585         // configuration attributes
586         this.config = config || {};
587
588         // create a local reference to the drag and drop manager
589         this.DDM = Roo.dd.DDM;
590         // initialize the groups array
591         this.groups = {};
592
593         // assume that we have an element reference instead of an id if the
594         // parameter is not a string
595         if (typeof id !== "string") {
596             id = Roo.id(id);
597         }
598
599         // set the id
600         this.id = id;
601
602         // add to an interaction group
603         this.addToGroup((sGroup) ? sGroup : "default");
604
605         // We don't want to register this as the handle with the manager
606         // so we just set the id rather than calling the setter.
607         this.handleElId = id;
608
609         // the linked element is the element that gets dragged by default
610         this.setDragElId(id);
611
612         // by default, clicked anchors will not start drag operations.
613         this.invalidHandleTypes = { A: "A" };
614         this.invalidHandleIds = {};
615         this.invalidHandleClasses = [];
616
617         this.applyConfig();
618
619         this.handleOnAvailable();
620     },
621
622     /**
623      * Applies the configuration parameters that were passed into the constructor.
624      * This is supposed to happen at each level through the inheritance chain.  So
625      * a DDProxy implentation will execute apply config on DDProxy, DD, and
626      * DragDrop in order to get all of the parameters that are available in
627      * each object.
628      * @method applyConfig
629      */
630     applyConfig: function() {
631
632         // configurable properties:
633         //    padding, isTarget, maintainOffset, primaryButtonOnly
634         this.padding           = this.config.padding || [0, 0, 0, 0];
635         this.isTarget          = (this.config.isTarget !== false);
636         this.maintainOffset    = (this.config.maintainOffset);
637         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
638
639     },
640
641     /**
642      * Executed when the linked element is available
643      * @method handleOnAvailable
644      * @private
645      */
646     handleOnAvailable: function() {
647         this.available = true;
648         this.resetConstraints();
649         this.onAvailable();
650     },
651
652      /**
653      * Configures the padding for the target zone in px.  Effectively expands
654      * (or reduces) the virtual object size for targeting calculations.
655      * Supports css-style shorthand; if only one parameter is passed, all sides
656      * will have that padding, and if only two are passed, the top and bottom
657      * will have the first param, the left and right the second.
658      * @method setPadding
659      * @param {int} iTop    Top pad
660      * @param {int} iRight  Right pad
661      * @param {int} iBot    Bot pad
662      * @param {int} iLeft   Left pad
663      */
664     setPadding: function(iTop, iRight, iBot, iLeft) {
665         // this.padding = [iLeft, iRight, iTop, iBot];
666         if (!iRight && 0 !== iRight) {
667             this.padding = [iTop, iTop, iTop, iTop];
668         } else if (!iBot && 0 !== iBot) {
669             this.padding = [iTop, iRight, iTop, iRight];
670         } else {
671             this.padding = [iTop, iRight, iBot, iLeft];
672         }
673     },
674
675     /**
676      * Stores the initial placement of the linked element.
677      * @method setInitialPosition
678      * @param {int} diffX   the X offset, default 0
679      * @param {int} diffY   the Y offset, default 0
680      */
681     setInitPosition: function(diffX, diffY) {
682         var el = this.getEl();
683
684         if (!this.DDM.verifyEl(el)) {
685             return;
686         }
687
688         var dx = diffX || 0;
689         var dy = diffY || 0;
690
691         var p = Dom.getXY( el );
692
693         this.initPageX = p[0] - dx;
694         this.initPageY = p[1] - dy;
695
696         this.lastPageX = p[0];
697         this.lastPageY = p[1];
698
699
700         this.setStartPosition(p);
701     },
702
703     /**
704      * Sets the start position of the element.  This is set when the obj
705      * is initialized, the reset when a drag is started.
706      * @method setStartPosition
707      * @param pos current position (from previous lookup)
708      * @private
709      */
710     setStartPosition: function(pos) {
711         var p = pos || Dom.getXY( this.getEl() );
712         this.deltaSetXY = null;
713
714         this.startPageX = p[0];
715         this.startPageY = p[1];
716     },
717
718     /**
719      * Add this instance to a group of related drag/drop objects.  All
720      * instances belong to at least one group, and can belong to as many
721      * groups as needed.
722      * @method addToGroup
723      * @param sGroup {string} the name of the group
724      */
725     addToGroup: function(sGroup) {
726         this.groups[sGroup] = true;
727         this.DDM.regDragDrop(this, sGroup);
728     },
729
730     /**
731      * Remove's this instance from the supplied interaction group
732      * @method removeFromGroup
733      * @param {string}  sGroup  The group to drop
734      */
735     removeFromGroup: function(sGroup) {
736         if (this.groups[sGroup]) {
737             delete this.groups[sGroup];
738         }
739
740         this.DDM.removeDDFromGroup(this, sGroup);
741     },
742
743     /**
744      * Allows you to specify that an element other than the linked element
745      * will be moved with the cursor during a drag
746      * @method setDragElId
747      * @param id {string} the id of the element that will be used to initiate the drag
748      */
749     setDragElId: function(id) {
750         this.dragElId = id;
751     },
752
753     /**
754      * Allows you to specify a child of the linked element that should be
755      * used to initiate the drag operation.  An example of this would be if
756      * you have a content div with text and links.  Clicking anywhere in the
757      * content area would normally start the drag operation.  Use this method
758      * to specify that an element inside of the content div is the element
759      * that starts the drag operation.
760      * @method setHandleElId
761      * @param id {string} the id of the element that will be used to
762      * initiate the drag.
763      */
764     setHandleElId: function(id) {
765         if (typeof id !== "string") {
766             id = Roo.id(id);
767         }
768         this.handleElId = id;
769         this.DDM.regHandle(this.id, id);
770     },
771
772     /**
773      * Allows you to set an element outside of the linked element as a drag
774      * handle
775      * @method setOuterHandleElId
776      * @param id the id of the element that will be used to initiate the drag
777      */
778     setOuterHandleElId: function(id) {
779         if (typeof id !== "string") {
780             id = Roo.id(id);
781         }
782         Event.on(id, "mousedown",
783                 this.handleMouseDown, this);
784         this.setHandleElId(id);
785
786         this.hasOuterHandles = true;
787     },
788
789     /**
790      * Remove all drag and drop hooks for this element
791      * @method unreg
792      */
793     unreg: function() {
794         Event.un(this.id, "mousedown",
795                 this.handleMouseDown);
796         Event.un(this.id, "touchstart",
797                 this.handleMouseDown);
798         this._domRef = null;
799         this.DDM._remove(this);
800     },
801
802     destroy : function(){
803         this.unreg();
804     },
805
806     /**
807      * Returns true if this instance is locked, or the drag drop mgr is locked
808      * (meaning that all drag/drop is disabled on the page.)
809      * @method isLocked
810      * @return {boolean} true if this obj or all drag/drop is locked, else
811      * false
812      */
813     isLocked: function() {
814         return (this.DDM.isLocked() || this.locked);
815     },
816
817     /**
818      * Fired when this object is clicked
819      * @method handleMouseDown
820      * @param {Event} e
821      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
822      * @private
823      */
824     handleMouseDown: function(e, oDD){
825      
826         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
827             //Roo.log('not touch/ button !=0');
828             return;
829         }
830         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
831             return; // double touch..
832         }
833         
834
835         if (this.isLocked()) {
836             //Roo.log('locked');
837             return;
838         }
839
840         this.DDM.refreshCache(this.groups);
841 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
842         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
843         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
844             //Roo.log('no outer handes or not over target');
845                 // do nothing.
846         } else {
847 //            Roo.log('check validator');
848             if (this.clickValidator(e)) {
849 //                Roo.log('validate success');
850                 // set the initial element position
851                 this.setStartPosition();
852
853
854                 this.b4MouseDown(e);
855                 this.onMouseDown(e);
856
857                 this.DDM.handleMouseDown(e, this);
858
859                 this.DDM.stopEvent(e);
860             } else {
861
862
863             }
864         }
865     },
866
867     clickValidator: function(e) {
868         var target = e.getTarget();
869         return ( this.isValidHandleChild(target) &&
870                     (this.id == this.handleElId ||
871                         this.DDM.handleWasClicked(target, this.id)) );
872     },
873
874     /**
875      * Allows you to specify a tag name that should not start a drag operation
876      * when clicked.  This is designed to facilitate embedding links within a
877      * drag handle that do something other than start the drag.
878      * @method addInvalidHandleType
879      * @param {string} tagName the type of element to exclude
880      */
881     addInvalidHandleType: function(tagName) {
882         var type = tagName.toUpperCase();
883         this.invalidHandleTypes[type] = type;
884     },
885
886     /**
887      * Lets you to specify an element id for a child of a drag handle
888      * that should not initiate a drag
889      * @method addInvalidHandleId
890      * @param {string} id the element id of the element you wish to ignore
891      */
892     addInvalidHandleId: function(id) {
893         if (typeof id !== "string") {
894             id = Roo.id(id);
895         }
896         this.invalidHandleIds[id] = id;
897     },
898
899     /**
900      * Lets you specify a css class of elements that will not initiate a drag
901      * @method addInvalidHandleClass
902      * @param {string} cssClass the class of the elements you wish to ignore
903      */
904     addInvalidHandleClass: function(cssClass) {
905         this.invalidHandleClasses.push(cssClass);
906     },
907
908     /**
909      * Unsets an excluded tag name set by addInvalidHandleType
910      * @method removeInvalidHandleType
911      * @param {string} tagName the type of element to unexclude
912      */
913     removeInvalidHandleType: function(tagName) {
914         var type = tagName.toUpperCase();
915         // this.invalidHandleTypes[type] = null;
916         delete this.invalidHandleTypes[type];
917     },
918
919     /**
920      * Unsets an invalid handle id
921      * @method removeInvalidHandleId
922      * @param {string} id the id of the element to re-enable
923      */
924     removeInvalidHandleId: function(id) {
925         if (typeof id !== "string") {
926             id = Roo.id(id);
927         }
928         delete this.invalidHandleIds[id];
929     },
930
931     /**
932      * Unsets an invalid css class
933      * @method removeInvalidHandleClass
934      * @param {string} cssClass the class of the element(s) you wish to
935      * re-enable
936      */
937     removeInvalidHandleClass: function(cssClass) {
938         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
939             if (this.invalidHandleClasses[i] == cssClass) {
940                 delete this.invalidHandleClasses[i];
941             }
942         }
943     },
944
945     /**
946      * Checks the tag exclusion list to see if this click should be ignored
947      * @method isValidHandleChild
948      * @param {HTMLElement} node the HTMLElement to evaluate
949      * @return {boolean} true if this is a valid tag type, false if not
950      */
951     isValidHandleChild: function(node) {
952
953         var valid = true;
954         // var n = (node.nodeName == "#text") ? node.parentNode : node;
955         var nodeName;
956         try {
957             nodeName = node.nodeName.toUpperCase();
958         } catch(e) {
959             nodeName = node.nodeName;
960         }
961         valid = valid && !this.invalidHandleTypes[nodeName];
962         valid = valid && !this.invalidHandleIds[node.id];
963
964         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
965             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
966         }
967
968
969         return valid;
970
971     },
972
973     /**
974      * Create the array of horizontal tick marks if an interval was specified
975      * in setXConstraint().
976      * @method setXTicks
977      * @private
978      */
979     setXTicks: function(iStartX, iTickSize) {
980         this.xTicks = [];
981         this.xTickSize = iTickSize;
982
983         var tickMap = {};
984
985         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
986             if (!tickMap[i]) {
987                 this.xTicks[this.xTicks.length] = i;
988                 tickMap[i] = true;
989             }
990         }
991
992         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
993             if (!tickMap[i]) {
994                 this.xTicks[this.xTicks.length] = i;
995                 tickMap[i] = true;
996             }
997         }
998
999         this.xTicks.sort(this.DDM.numericSort) ;
1000     },
1001
1002     /**
1003      * Create the array of vertical tick marks if an interval was specified in
1004      * setYConstraint().
1005      * @method setYTicks
1006      * @private
1007      */
1008     setYTicks: function(iStartY, iTickSize) {
1009         this.yTicks = [];
1010         this.yTickSize = iTickSize;
1011
1012         var tickMap = {};
1013
1014         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1015             if (!tickMap[i]) {
1016                 this.yTicks[this.yTicks.length] = i;
1017                 tickMap[i] = true;
1018             }
1019         }
1020
1021         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1022             if (!tickMap[i]) {
1023                 this.yTicks[this.yTicks.length] = i;
1024                 tickMap[i] = true;
1025             }
1026         }
1027
1028         this.yTicks.sort(this.DDM.numericSort) ;
1029     },
1030
1031     /**
1032      * By default, the element can be dragged any place on the screen.  Use
1033      * this method to limit the horizontal travel of the element.  Pass in
1034      * 0,0 for the parameters if you want to lock the drag to the y axis.
1035      * @method setXConstraint
1036      * @param {int} iLeft the number of pixels the element can move to the left
1037      * @param {int} iRight the number of pixels the element can move to the
1038      * right
1039      * @param {int} iTickSize optional parameter for specifying that the
1040      * element
1041      * should move iTickSize pixels at a time.
1042      */
1043     setXConstraint: function(iLeft, iRight, iTickSize) {
1044         this.leftConstraint = iLeft;
1045         this.rightConstraint = iRight;
1046
1047         this.minX = this.initPageX - iLeft;
1048         this.maxX = this.initPageX + iRight;
1049         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1050
1051         this.constrainX = true;
1052     },
1053
1054     /**
1055      * Clears any constraints applied to this instance.  Also clears ticks
1056      * since they can't exist independent of a constraint at this time.
1057      * @method clearConstraints
1058      */
1059     clearConstraints: function() {
1060         this.constrainX = false;
1061         this.constrainY = false;
1062         this.clearTicks();
1063     },
1064
1065     /**
1066      * Clears any tick interval defined for this instance
1067      * @method clearTicks
1068      */
1069     clearTicks: function() {
1070         this.xTicks = null;
1071         this.yTicks = null;
1072         this.xTickSize = 0;
1073         this.yTickSize = 0;
1074     },
1075
1076     /**
1077      * By default, the element can be dragged any place on the screen.  Set
1078      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1079      * parameters if you want to lock the drag to the x axis.
1080      * @method setYConstraint
1081      * @param {int} iUp the number of pixels the element can move up
1082      * @param {int} iDown the number of pixels the element can move down
1083      * @param {int} iTickSize optional parameter for specifying that the
1084      * element should move iTickSize pixels at a time.
1085      */
1086     setYConstraint: function(iUp, iDown, iTickSize) {
1087         this.topConstraint = iUp;
1088         this.bottomConstraint = iDown;
1089
1090         this.minY = this.initPageY - iUp;
1091         this.maxY = this.initPageY + iDown;
1092         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1093
1094         this.constrainY = true;
1095
1096     },
1097
1098     /**
1099      * resetConstraints must be called if you manually reposition a dd element.
1100      * @method resetConstraints
1101      * @param {boolean} maintainOffset
1102      */
1103     resetConstraints: function() {
1104
1105
1106         // Maintain offsets if necessary
1107         if (this.initPageX || this.initPageX === 0) {
1108             // figure out how much this thing has moved
1109             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1110             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1111
1112             this.setInitPosition(dx, dy);
1113
1114         // This is the first time we have detected the element's position
1115         } else {
1116             this.setInitPosition();
1117         }
1118
1119         if (this.constrainX) {
1120             this.setXConstraint( this.leftConstraint,
1121                                  this.rightConstraint,
1122                                  this.xTickSize        );
1123         }
1124
1125         if (this.constrainY) {
1126             this.setYConstraint( this.topConstraint,
1127                                  this.bottomConstraint,
1128                                  this.yTickSize         );
1129         }
1130     },
1131
1132     /**
1133      * Normally the drag element is moved pixel by pixel, but we can specify
1134      * that it move a number of pixels at a time.  This method resolves the
1135      * location when we have it set up like this.
1136      * @method getTick
1137      * @param {int} val where we want to place the object
1138      * @param {int[]} tickArray sorted array of valid points
1139      * @return {int} the closest tick
1140      * @private
1141      */
1142     getTick: function(val, tickArray) {
1143
1144         if (!tickArray) {
1145             // If tick interval is not defined, it is effectively 1 pixel,
1146             // so we return the value passed to us.
1147             return val;
1148         } else if (tickArray[0] >= val) {
1149             // The value is lower than the first tick, so we return the first
1150             // tick.
1151             return tickArray[0];
1152         } else {
1153             for (var i=0, len=tickArray.length; i<len; ++i) {
1154                 var next = i + 1;
1155                 if (tickArray[next] && tickArray[next] >= val) {
1156                     var diff1 = val - tickArray[i];
1157                     var diff2 = tickArray[next] - val;
1158                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1159                 }
1160             }
1161
1162             // The value is larger than the last tick, so we return the last
1163             // tick.
1164             return tickArray[tickArray.length - 1];
1165         }
1166     },
1167
1168     /**
1169      * toString method
1170      * @method toString
1171      * @return {string} string representation of the dd obj
1172      */
1173     toString: function() {
1174         return ("DragDrop " + this.id);
1175     }
1176
1177 });
1178
1179 })();
1180 /*
1181  * Based on:
1182  * Ext JS Library 1.1.1
1183  * Copyright(c) 2006-2007, Ext JS, LLC.
1184  *
1185  * Originally Released Under LGPL - original licence link has changed is not relivant.
1186  *
1187  * Fork - LGPL
1188  * <script type="text/javascript">
1189  */
1190
1191
1192 /**
1193  * The drag and drop utility provides a framework for building drag and drop
1194  * applications.  In addition to enabling drag and drop for specific elements,
1195  * the drag and drop elements are tracked by the manager class, and the
1196  * interactions between the various elements are tracked during the drag and
1197  * the implementing code is notified about these important moments.
1198  */
1199
1200 // Only load the library once.  Rewriting the manager class would orphan
1201 // existing drag and drop instances.
1202 if (!Roo.dd.DragDropMgr) {
1203
1204 /**
1205  * @class Roo.dd.DragDropMgr
1206  * DragDropMgr is a singleton that tracks the element interaction for
1207  * all DragDrop items in the window.  Generally, you will not call
1208  * this class directly, but it does have helper methods that could
1209  * be useful in your DragDrop implementations.
1210  * @singleton
1211  */
1212 Roo.dd.DragDropMgr = function() {
1213
1214     var Event = Roo.EventManager;
1215
1216     return {
1217
1218         /**
1219          * Two dimensional Array of registered DragDrop objects.  The first
1220          * dimension is the DragDrop item group, the second the DragDrop
1221          * object.
1222          * @property ids
1223          * @type {string: string}
1224          * @private
1225          * @static
1226          */
1227         ids: {},
1228
1229         /**
1230          * Array of element ids defined as drag handles.  Used to determine
1231          * if the element that generated the mousedown event is actually the
1232          * handle and not the html element itself.
1233          * @property handleIds
1234          * @type {string: string}
1235          * @private
1236          * @static
1237          */
1238         handleIds: {},
1239
1240         /**
1241          * the DragDrop object that is currently being dragged
1242          * @property dragCurrent
1243          * @type DragDrop
1244          * @private
1245          * @static
1246          **/
1247         dragCurrent: null,
1248
1249         /**
1250          * the DragDrop object(s) that are being hovered over
1251          * @property dragOvers
1252          * @type Array
1253          * @private
1254          * @static
1255          */
1256         dragOvers: {},
1257
1258         /**
1259          * the X distance between the cursor and the object being dragged
1260          * @property deltaX
1261          * @type int
1262          * @private
1263          * @static
1264          */
1265         deltaX: 0,
1266
1267         /**
1268          * the Y distance between the cursor and the object being dragged
1269          * @property deltaY
1270          * @type int
1271          * @private
1272          * @static
1273          */
1274         deltaY: 0,
1275
1276         /**
1277          * Flag to determine if we should prevent the default behavior of the
1278          * events we define. By default this is true, but this can be set to
1279          * false if you need the default behavior (not recommended)
1280          * @property preventDefault
1281          * @type boolean
1282          * @static
1283          */
1284         preventDefault: true,
1285
1286         /**
1287          * Flag to determine if we should stop the propagation of the events
1288          * we generate. This is true by default but you may want to set it to
1289          * false if the html element contains other features that require the
1290          * mouse click.
1291          * @property stopPropagation
1292          * @type boolean
1293          * @static
1294          */
1295         stopPropagation: true,
1296
1297         /**
1298          * Internal flag that is set to true when drag and drop has been
1299          * intialized
1300          * @property initialized
1301          * @private
1302          * @static
1303          */
1304         initalized: false,
1305
1306         /**
1307          * All drag and drop can be disabled.
1308          * @property locked
1309          * @private
1310          * @static
1311          */
1312         locked: false,
1313
1314         /**
1315          * Called the first time an element is registered.
1316          * @method init
1317          * @private
1318          * @static
1319          */
1320         init: function() {
1321             this.initialized = true;
1322         },
1323
1324         /**
1325          * In point mode, drag and drop interaction is defined by the
1326          * location of the cursor during the drag/drop
1327          * @property POINT
1328          * @type int
1329          * @static
1330          */
1331         POINT: 0,
1332
1333         /**
1334          * In intersect mode, drag and drop interactio nis defined by the
1335          * overlap of two or more drag and drop objects.
1336          * @property INTERSECT
1337          * @type int
1338          * @static
1339          */
1340         INTERSECT: 1,
1341
1342         /**
1343          * The current drag and drop mode.  Default: POINT
1344          * @property mode
1345          * @type int
1346          * @static
1347          */
1348         mode: 0,
1349
1350         /**
1351          * Runs method on all drag and drop objects
1352          * @method _execOnAll
1353          * @private
1354          * @static
1355          */
1356         _execOnAll: function(sMethod, args) {
1357             for (var i in this.ids) {
1358                 for (var j in this.ids[i]) {
1359                     var oDD = this.ids[i][j];
1360                     if (! this.isTypeOfDD(oDD)) {
1361                         continue;
1362                     }
1363                     oDD[sMethod].apply(oDD, args);
1364                 }
1365             }
1366         },
1367
1368         /**
1369          * Drag and drop initialization.  Sets up the global event handlers
1370          * @method _onLoad
1371          * @private
1372          * @static
1373          */
1374         _onLoad: function() {
1375
1376             this.init();
1377
1378             if (!Roo.isTouch) {
1379                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1380                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
1381             }
1382             Event.on(document, "touchend",   this.handleMouseUp, this, true);
1383             Event.on(document, "touchmove", this.handleMouseMove, this, true);
1384             
1385             Event.on(window,   "unload",    this._onUnload, this, true);
1386             Event.on(window,   "resize",    this._onResize, this, true);
1387             // Event.on(window,   "mouseout",    this._test);
1388
1389         },
1390
1391         /**
1392          * Reset constraints on all drag and drop objs
1393          * @method _onResize
1394          * @private
1395          * @static
1396          */
1397         _onResize: function(e) {
1398             this._execOnAll("resetConstraints", []);
1399         },
1400
1401         /**
1402          * Lock all drag and drop functionality
1403          * @method lock
1404          * @static
1405          */
1406         lock: function() { this.locked = true; },
1407
1408         /**
1409          * Unlock all drag and drop functionality
1410          * @method unlock
1411          * @static
1412          */
1413         unlock: function() { this.locked = false; },
1414
1415         /**
1416          * Is drag and drop locked?
1417          * @method isLocked
1418          * @return {boolean} True if drag and drop is locked, false otherwise.
1419          * @static
1420          */
1421         isLocked: function() { return this.locked; },
1422
1423         /**
1424          * Location cache that is set for all drag drop objects when a drag is
1425          * initiated, cleared when the drag is finished.
1426          * @property locationCache
1427          * @private
1428          * @static
1429          */
1430         locationCache: {},
1431
1432         /**
1433          * Set useCache to false if you want to force object the lookup of each
1434          * drag and drop linked element constantly during a drag.
1435          * @property useCache
1436          * @type boolean
1437          * @static
1438          */
1439         useCache: true,
1440
1441         /**
1442          * The number of pixels that the mouse needs to move after the
1443          * mousedown before the drag is initiated.  Default=3;
1444          * @property clickPixelThresh
1445          * @type int
1446          * @static
1447          */
1448         clickPixelThresh: 3,
1449
1450         /**
1451          * The number of milliseconds after the mousedown event to initiate the
1452          * drag if we don't get a mouseup event. Default=1000
1453          * @property clickTimeThresh
1454          * @type int
1455          * @static
1456          */
1457         clickTimeThresh: 350,
1458
1459         /**
1460          * Flag that indicates that either the drag pixel threshold or the
1461          * mousdown time threshold has been met
1462          * @property dragThreshMet
1463          * @type boolean
1464          * @private
1465          * @static
1466          */
1467         dragThreshMet: false,
1468
1469         /**
1470          * Timeout used for the click time threshold
1471          * @property clickTimeout
1472          * @type Object
1473          * @private
1474          * @static
1475          */
1476         clickTimeout: null,
1477
1478         /**
1479          * The X position of the mousedown event stored for later use when a
1480          * drag threshold is met.
1481          * @property startX
1482          * @type int
1483          * @private
1484          * @static
1485          */
1486         startX: 0,
1487
1488         /**
1489          * The Y position of the mousedown event stored for later use when a
1490          * drag threshold is met.
1491          * @property startY
1492          * @type int
1493          * @private
1494          * @static
1495          */
1496         startY: 0,
1497
1498         /**
1499          * Each DragDrop instance must be registered with the DragDropMgr.
1500          * This is executed in DragDrop.init()
1501          * @method regDragDrop
1502          * @param {DragDrop} oDD the DragDrop object to register
1503          * @param {String} sGroup the name of the group this element belongs to
1504          * @static
1505          */
1506         regDragDrop: function(oDD, sGroup) {
1507             if (!this.initialized) { this.init(); }
1508
1509             if (!this.ids[sGroup]) {
1510                 this.ids[sGroup] = {};
1511             }
1512             this.ids[sGroup][oDD.id] = oDD;
1513         },
1514
1515         /**
1516          * Removes the supplied dd instance from the supplied group. Executed
1517          * by DragDrop.removeFromGroup, so don't call this function directly.
1518          * @method removeDDFromGroup
1519          * @private
1520          * @static
1521          */
1522         removeDDFromGroup: function(oDD, sGroup) {
1523             if (!this.ids[sGroup]) {
1524                 this.ids[sGroup] = {};
1525             }
1526
1527             var obj = this.ids[sGroup];
1528             if (obj && obj[oDD.id]) {
1529                 delete obj[oDD.id];
1530             }
1531         },
1532
1533         /**
1534          * Unregisters a drag and drop item.  This is executed in
1535          * DragDrop.unreg, use that method instead of calling this directly.
1536          * @method _remove
1537          * @private
1538          * @static
1539          */
1540         _remove: function(oDD) {
1541             for (var g in oDD.groups) {
1542                 if (g && this.ids[g][oDD.id]) {
1543                     delete this.ids[g][oDD.id];
1544                 }
1545             }
1546             delete this.handleIds[oDD.id];
1547         },
1548
1549         /**
1550          * Each DragDrop handle element must be registered.  This is done
1551          * automatically when executing DragDrop.setHandleElId()
1552          * @method regHandle
1553          * @param {String} sDDId the DragDrop id this element is a handle for
1554          * @param {String} sHandleId the id of the element that is the drag
1555          * handle
1556          * @static
1557          */
1558         regHandle: function(sDDId, sHandleId) {
1559             if (!this.handleIds[sDDId]) {
1560                 this.handleIds[sDDId] = {};
1561             }
1562             this.handleIds[sDDId][sHandleId] = sHandleId;
1563         },
1564
1565         /**
1566          * Utility function to determine if a given element has been
1567          * registered as a drag drop item.
1568          * @method isDragDrop
1569          * @param {String} id the element id to check
1570          * @return {boolean} true if this element is a DragDrop item,
1571          * false otherwise
1572          * @static
1573          */
1574         isDragDrop: function(id) {
1575             return ( this.getDDById(id) ) ? true : false;
1576         },
1577
1578         /**
1579          * Returns the drag and drop instances that are in all groups the
1580          * passed in instance belongs to.
1581          * @method getRelated
1582          * @param {DragDrop} p_oDD the obj to get related data for
1583          * @param {boolean} bTargetsOnly if true, only return targetable objs
1584          * @return {DragDrop[]} the related instances
1585          * @static
1586          */
1587         getRelated: function(p_oDD, bTargetsOnly) {
1588             var oDDs = [];
1589             for (var i in p_oDD.groups) {
1590                 for (j in this.ids[i]) {
1591                     var dd = this.ids[i][j];
1592                     if (! this.isTypeOfDD(dd)) {
1593                         continue;
1594                     }
1595                     if (!bTargetsOnly || dd.isTarget) {
1596                         oDDs[oDDs.length] = dd;
1597                     }
1598                 }
1599             }
1600
1601             return oDDs;
1602         },
1603
1604         /**
1605          * Returns true if the specified dd target is a legal target for
1606          * the specifice drag obj
1607          * @method isLegalTarget
1608          * @param {DragDrop} the drag obj
1609          * @param {DragDrop} the target
1610          * @return {boolean} true if the target is a legal target for the
1611          * dd obj
1612          * @static
1613          */
1614         isLegalTarget: function (oDD, oTargetDD) {
1615             var targets = this.getRelated(oDD, true);
1616             for (var i=0, len=targets.length;i<len;++i) {
1617                 if (targets[i].id == oTargetDD.id) {
1618                     return true;
1619                 }
1620             }
1621
1622             return false;
1623         },
1624
1625         /**
1626          * My goal is to be able to transparently determine if an object is
1627          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1628          * returns "object", oDD.constructor.toString() always returns
1629          * "DragDrop" and not the name of the subclass.  So for now it just
1630          * evaluates a well-known variable in DragDrop.
1631          * @method isTypeOfDD
1632          * @param {Object} the object to evaluate
1633          * @return {boolean} true if typeof oDD = DragDrop
1634          * @static
1635          */
1636         isTypeOfDD: function (oDD) {
1637             return (oDD && oDD.__ygDragDrop);
1638         },
1639
1640         /**
1641          * Utility function to determine if a given element has been
1642          * registered as a drag drop handle for the given Drag Drop object.
1643          * @method isHandle
1644          * @param {String} id the element id to check
1645          * @return {boolean} true if this element is a DragDrop handle, false
1646          * otherwise
1647          * @static
1648          */
1649         isHandle: function(sDDId, sHandleId) {
1650             return ( this.handleIds[sDDId] &&
1651                             this.handleIds[sDDId][sHandleId] );
1652         },
1653
1654         /**
1655          * Returns the DragDrop instance for a given id
1656          * @method getDDById
1657          * @param {String} id the id of the DragDrop object
1658          * @return {DragDrop} the drag drop object, null if it is not found
1659          * @static
1660          */
1661         getDDById: function(id) {
1662             for (var i in this.ids) {
1663                 if (this.ids[i][id]) {
1664                     return this.ids[i][id];
1665                 }
1666             }
1667             return null;
1668         },
1669
1670         /**
1671          * Fired after a registered DragDrop object gets the mousedown event.
1672          * Sets up the events required to track the object being dragged
1673          * @method handleMouseDown
1674          * @param {Event} e the event
1675          * @param oDD the DragDrop object being dragged
1676          * @private
1677          * @static
1678          */
1679         handleMouseDown: function(e, oDD) {
1680             if(Roo.QuickTips){
1681                 Roo.QuickTips.disable();
1682             }
1683             this.currentTarget = e.getTarget();
1684
1685             this.dragCurrent = oDD;
1686
1687             var el = oDD.getEl();
1688
1689             // track start position
1690             this.startX = e.getPageX();
1691             this.startY = e.getPageY();
1692
1693             this.deltaX = this.startX - el.offsetLeft;
1694             this.deltaY = this.startY - el.offsetTop;
1695
1696             this.dragThreshMet = false;
1697
1698             this.clickTimeout = setTimeout(
1699                     function() {
1700                         var DDM = Roo.dd.DDM;
1701                         DDM.startDrag(DDM.startX, DDM.startY);
1702                     },
1703                     this.clickTimeThresh );
1704         },
1705
1706         /**
1707          * Fired when either the drag pixel threshol or the mousedown hold
1708          * time threshold has been met.
1709          * @method startDrag
1710          * @param x {int} the X position of the original mousedown
1711          * @param y {int} the Y position of the original mousedown
1712          * @static
1713          */
1714         startDrag: function(x, y) {
1715             clearTimeout(this.clickTimeout);
1716             if (this.dragCurrent) {
1717                 this.dragCurrent.b4StartDrag(x, y);
1718                 this.dragCurrent.startDrag(x, y);
1719             }
1720             this.dragThreshMet = true;
1721         },
1722
1723         /**
1724          * Internal function to handle the mouseup event.  Will be invoked
1725          * from the context of the document.
1726          * @method handleMouseUp
1727          * @param {Event} e the event
1728          * @private
1729          * @static
1730          */
1731         handleMouseUp: function(e) {
1732
1733             if(Roo.QuickTips){
1734                 Roo.QuickTips.enable();
1735             }
1736             if (! this.dragCurrent) {
1737                 return;
1738             }
1739
1740             clearTimeout(this.clickTimeout);
1741
1742             if (this.dragThreshMet) {
1743                 this.fireEvents(e, true);
1744             } else {
1745             }
1746
1747             this.stopDrag(e);
1748
1749             this.stopEvent(e);
1750         },
1751
1752         /**
1753          * Utility to stop event propagation and event default, if these
1754          * features are turned on.
1755          * @method stopEvent
1756          * @param {Event} e the event as returned by this.getEvent()
1757          * @static
1758          */
1759         stopEvent: function(e){
1760             if(this.stopPropagation) {
1761                 e.stopPropagation();
1762             }
1763
1764             if (this.preventDefault) {
1765                 e.preventDefault();
1766             }
1767         },
1768
1769         /**
1770          * Internal function to clean up event handlers after the drag
1771          * operation is complete
1772          * @method stopDrag
1773          * @param {Event} e the event
1774          * @private
1775          * @static
1776          */
1777         stopDrag: function(e) {
1778             // Fire the drag end event for the item that was dragged
1779             if (this.dragCurrent) {
1780                 if (this.dragThreshMet) {
1781                     this.dragCurrent.b4EndDrag(e);
1782                     this.dragCurrent.endDrag(e);
1783                 }
1784
1785                 this.dragCurrent.onMouseUp(e);
1786             }
1787
1788             this.dragCurrent = null;
1789             this.dragOvers = {};
1790         },
1791
1792         /**
1793          * Internal function to handle the mousemove event.  Will be invoked
1794          * from the context of the html element.
1795          *
1796          * @TODO figure out what we can do about mouse events lost when the
1797          * user drags objects beyond the window boundary.  Currently we can
1798          * detect this in internet explorer by verifying that the mouse is
1799          * down during the mousemove event.  Firefox doesn't give us the
1800          * button state on the mousemove event.
1801          * @method handleMouseMove
1802          * @param {Event} e the event
1803          * @private
1804          * @static
1805          */
1806         handleMouseMove: function(e) {
1807             if (! this.dragCurrent) {
1808                 return true;
1809             }
1810
1811             // var button = e.which || e.button;
1812
1813             // check for IE mouseup outside of page boundary
1814             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1815                 this.stopEvent(e);
1816                 return this.handleMouseUp(e);
1817             }
1818
1819             if (!this.dragThreshMet) {
1820                 var diffX = Math.abs(this.startX - e.getPageX());
1821                 var diffY = Math.abs(this.startY - e.getPageY());
1822                 if (diffX > this.clickPixelThresh ||
1823                             diffY > this.clickPixelThresh) {
1824                     this.startDrag(this.startX, this.startY);
1825                 }
1826             }
1827
1828             if (this.dragThreshMet) {
1829                 this.dragCurrent.b4Drag(e);
1830                 this.dragCurrent.onDrag(e);
1831                 if(!this.dragCurrent.moveOnly){
1832                     this.fireEvents(e, false);
1833                 }
1834             }
1835
1836             this.stopEvent(e);
1837
1838             return true;
1839         },
1840
1841         /**
1842          * Iterates over all of the DragDrop elements to find ones we are
1843          * hovering over or dropping on
1844          * @method fireEvents
1845          * @param {Event} e the event
1846          * @param {boolean} isDrop is this a drop op or a mouseover op?
1847          * @private
1848          * @static
1849          */
1850         fireEvents: function(e, isDrop) {
1851             var dc = this.dragCurrent;
1852
1853             // If the user did the mouse up outside of the window, we could
1854             // get here even though we have ended the drag.
1855             if (!dc || dc.isLocked()) {
1856                 return;
1857             }
1858
1859             var pt = e.getPoint();
1860
1861             // cache the previous dragOver array
1862             var oldOvers = [];
1863
1864             var outEvts   = [];
1865             var overEvts  = [];
1866             var dropEvts  = [];
1867             var enterEvts = [];
1868
1869             // Check to see if the object(s) we were hovering over is no longer
1870             // being hovered over so we can fire the onDragOut event
1871             for (var i in this.dragOvers) {
1872
1873                 var ddo = this.dragOvers[i];
1874
1875                 if (! this.isTypeOfDD(ddo)) {
1876                     continue;
1877                 }
1878
1879                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1880                     outEvts.push( ddo );
1881                 }
1882
1883                 oldOvers[i] = true;
1884                 delete this.dragOvers[i];
1885             }
1886
1887             for (var sGroup in dc.groups) {
1888
1889                 if ("string" != typeof sGroup) {
1890                     continue;
1891                 }
1892
1893                 for (i in this.ids[sGroup]) {
1894                     var oDD = this.ids[sGroup][i];
1895                     if (! this.isTypeOfDD(oDD)) {
1896                         continue;
1897                     }
1898
1899                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1900                         if (this.isOverTarget(pt, oDD, this.mode)) {
1901                             // look for drop interactions
1902                             if (isDrop) {
1903                                 dropEvts.push( oDD );
1904                             // look for drag enter and drag over interactions
1905                             } else {
1906
1907                                 // initial drag over: dragEnter fires
1908                                 if (!oldOvers[oDD.id]) {
1909                                     enterEvts.push( oDD );
1910                                 // subsequent drag overs: dragOver fires
1911                                 } else {
1912                                     overEvts.push( oDD );
1913                                 }
1914
1915                                 this.dragOvers[oDD.id] = oDD;
1916                             }
1917                         }
1918                     }
1919                 }
1920             }
1921
1922             if (this.mode) {
1923                 if (outEvts.length) {
1924                     dc.b4DragOut(e, outEvts);
1925                     dc.onDragOut(e, outEvts);
1926                 }
1927
1928                 if (enterEvts.length) {
1929                     dc.onDragEnter(e, enterEvts);
1930                 }
1931
1932                 if (overEvts.length) {
1933                     dc.b4DragOver(e, overEvts);
1934                     dc.onDragOver(e, overEvts);
1935                 }
1936
1937                 if (dropEvts.length) {
1938                     dc.b4DragDrop(e, dropEvts);
1939                     dc.onDragDrop(e, dropEvts);
1940                 }
1941
1942             } else {
1943                 // fire dragout events
1944                 var len = 0;
1945                 for (i=0, len=outEvts.length; i<len; ++i) {
1946                     dc.b4DragOut(e, outEvts[i].id);
1947                     dc.onDragOut(e, outEvts[i].id);
1948                 }
1949
1950                 // fire enter events
1951                 for (i=0,len=enterEvts.length; i<len; ++i) {
1952                     // dc.b4DragEnter(e, oDD.id);
1953                     dc.onDragEnter(e, enterEvts[i].id);
1954                 }
1955
1956                 // fire over events
1957                 for (i=0,len=overEvts.length; i<len; ++i) {
1958                     dc.b4DragOver(e, overEvts[i].id);
1959                     dc.onDragOver(e, overEvts[i].id);
1960                 }
1961
1962                 // fire drop events
1963                 for (i=0, len=dropEvts.length; i<len; ++i) {
1964                     dc.b4DragDrop(e, dropEvts[i].id);
1965                     dc.onDragDrop(e, dropEvts[i].id);
1966                 }
1967
1968             }
1969
1970             // notify about a drop that did not find a target
1971             if (isDrop && !dropEvts.length) {
1972                 dc.onInvalidDrop(e);
1973             }
1974
1975         },
1976
1977         /**
1978          * Helper function for getting the best match from the list of drag
1979          * and drop objects returned by the drag and drop events when we are
1980          * in INTERSECT mode.  It returns either the first object that the
1981          * cursor is over, or the object that has the greatest overlap with
1982          * the dragged element.
1983          * @method getBestMatch
1984          * @param  {DragDrop[]} dds The array of drag and drop objects
1985          * targeted
1986          * @return {DragDrop}       The best single match
1987          * @static
1988          */
1989         getBestMatch: function(dds) {
1990             var winner = null;
1991             // Return null if the input is not what we expect
1992             //if (!dds || !dds.length || dds.length == 0) {
1993                // winner = null;
1994             // If there is only one item, it wins
1995             //} else if (dds.length == 1) {
1996
1997             var len = dds.length;
1998
1999             if (len == 1) {
2000                 winner = dds[0];
2001             } else {
2002                 // Loop through the targeted items
2003                 for (var i=0; i<len; ++i) {
2004                     var dd = dds[i];
2005                     // If the cursor is over the object, it wins.  If the
2006                     // cursor is over multiple matches, the first one we come
2007                     // to wins.
2008                     if (dd.cursorIsOver) {
2009                         winner = dd;
2010                         break;
2011                     // Otherwise the object with the most overlap wins
2012                     } else {
2013                         if (!winner ||
2014                             winner.overlap.getArea() < dd.overlap.getArea()) {
2015                             winner = dd;
2016                         }
2017                     }
2018                 }
2019             }
2020
2021             return winner;
2022         },
2023
2024         /**
2025          * Refreshes the cache of the top-left and bottom-right points of the
2026          * drag and drop objects in the specified group(s).  This is in the
2027          * format that is stored in the drag and drop instance, so typical
2028          * usage is:
2029          * <code>
2030          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2031          * </code>
2032          * Alternatively:
2033          * <code>
2034          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2035          * </code>
2036          * @TODO this really should be an indexed array.  Alternatively this
2037          * method could accept both.
2038          * @method refreshCache
2039          * @param {Object} groups an associative array of groups to refresh
2040          * @static
2041          */
2042         refreshCache: function(groups) {
2043             for (var sGroup in groups) {
2044                 if ("string" != typeof sGroup) {
2045                     continue;
2046                 }
2047                 for (var i in this.ids[sGroup]) {
2048                     var oDD = this.ids[sGroup][i];
2049
2050                     if (this.isTypeOfDD(oDD)) {
2051                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2052                         var loc = this.getLocation(oDD);
2053                         if (loc) {
2054                             this.locationCache[oDD.id] = loc;
2055                         } else {
2056                             delete this.locationCache[oDD.id];
2057                             // this will unregister the drag and drop object if
2058                             // the element is not in a usable state
2059                             // oDD.unreg();
2060                         }
2061                     }
2062                 }
2063             }
2064         },
2065
2066         /**
2067          * This checks to make sure an element exists and is in the DOM.  The
2068          * main purpose is to handle cases where innerHTML is used to remove
2069          * drag and drop objects from the DOM.  IE provides an 'unspecified
2070          * error' when trying to access the offsetParent of such an element
2071          * @method verifyEl
2072          * @param {HTMLElement} el the element to check
2073          * @return {boolean} true if the element looks usable
2074          * @static
2075          */
2076         verifyEl: function(el) {
2077             if (el) {
2078                 var parent;
2079                 if(Roo.isIE){
2080                     try{
2081                         parent = el.offsetParent;
2082                     }catch(e){}
2083                 }else{
2084                     parent = el.offsetParent;
2085                 }
2086                 if (parent) {
2087                     return true;
2088                 }
2089             }
2090
2091             return false;
2092         },
2093
2094         /**
2095          * Returns a Region object containing the drag and drop element's position
2096          * and size, including the padding configured for it
2097          * @method getLocation
2098          * @param {DragDrop} oDD the drag and drop object to get the
2099          *                       location for
2100          * @return {Roo.lib.Region} a Region object representing the total area
2101          *                             the element occupies, including any padding
2102          *                             the instance is configured for.
2103          * @static
2104          */
2105         getLocation: function(oDD) {
2106             if (! this.isTypeOfDD(oDD)) {
2107                 return null;
2108             }
2109
2110             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2111
2112             try {
2113                 pos= Roo.lib.Dom.getXY(el);
2114             } catch (e) { }
2115
2116             if (!pos) {
2117                 return null;
2118             }
2119
2120             x1 = pos[0];
2121             x2 = x1 + el.offsetWidth;
2122             y1 = pos[1];
2123             y2 = y1 + el.offsetHeight;
2124
2125             t = y1 - oDD.padding[0];
2126             r = x2 + oDD.padding[1];
2127             b = y2 + oDD.padding[2];
2128             l = x1 - oDD.padding[3];
2129
2130             return new Roo.lib.Region( t, r, b, l );
2131         },
2132
2133         /**
2134          * Checks the cursor location to see if it over the target
2135          * @method isOverTarget
2136          * @param {Roo.lib.Point} pt The point to evaluate
2137          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2138          * @return {boolean} true if the mouse is over the target
2139          * @private
2140          * @static
2141          */
2142         isOverTarget: function(pt, oTarget, intersect) {
2143             // use cache if available
2144             var loc = this.locationCache[oTarget.id];
2145             if (!loc || !this.useCache) {
2146                 loc = this.getLocation(oTarget);
2147                 this.locationCache[oTarget.id] = loc;
2148
2149             }
2150
2151             if (!loc) {
2152                 return false;
2153             }
2154
2155             oTarget.cursorIsOver = loc.contains( pt );
2156
2157             // DragDrop is using this as a sanity check for the initial mousedown
2158             // in this case we are done.  In POINT mode, if the drag obj has no
2159             // contraints, we are also done. Otherwise we need to evaluate the
2160             // location of the target as related to the actual location of the
2161             // dragged element.
2162             var dc = this.dragCurrent;
2163             if (!dc || !dc.getTargetCoord ||
2164                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2165                 return oTarget.cursorIsOver;
2166             }
2167
2168             oTarget.overlap = null;
2169
2170             // Get the current location of the drag element, this is the
2171             // location of the mouse event less the delta that represents
2172             // where the original mousedown happened on the element.  We
2173             // need to consider constraints and ticks as well.
2174             var pos = dc.getTargetCoord(pt.x, pt.y);
2175
2176             var el = dc.getDragEl();
2177             var curRegion = new Roo.lib.Region( pos.y,
2178                                                    pos.x + el.offsetWidth,
2179                                                    pos.y + el.offsetHeight,
2180                                                    pos.x );
2181
2182             var overlap = curRegion.intersect(loc);
2183
2184             if (overlap) {
2185                 oTarget.overlap = overlap;
2186                 return (intersect) ? true : oTarget.cursorIsOver;
2187             } else {
2188                 return false;
2189             }
2190         },
2191
2192         /**
2193          * unload event handler
2194          * @method _onUnload
2195          * @private
2196          * @static
2197          */
2198         _onUnload: function(e, me) {
2199             Roo.dd.DragDropMgr.unregAll();
2200         },
2201
2202         /**
2203          * Cleans up the drag and drop events and objects.
2204          * @method unregAll
2205          * @private
2206          * @static
2207          */
2208         unregAll: function() {
2209
2210             if (this.dragCurrent) {
2211                 this.stopDrag();
2212                 this.dragCurrent = null;
2213             }
2214
2215             this._execOnAll("unreg", []);
2216
2217             for (i in this.elementCache) {
2218                 delete this.elementCache[i];
2219             }
2220
2221             this.elementCache = {};
2222             this.ids = {};
2223         },
2224
2225         /**
2226          * A cache of DOM elements
2227          * @property elementCache
2228          * @private
2229          * @static
2230          */
2231         elementCache: {},
2232
2233         /**
2234          * Get the wrapper for the DOM element specified
2235          * @method getElWrapper
2236          * @param {String} id the id of the element to get
2237          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2238          * @private
2239          * @deprecated This wrapper isn't that useful
2240          * @static
2241          */
2242         getElWrapper: function(id) {
2243             var oWrapper = this.elementCache[id];
2244             if (!oWrapper || !oWrapper.el) {
2245                 oWrapper = this.elementCache[id] =
2246                     new this.ElementWrapper(Roo.getDom(id));
2247             }
2248             return oWrapper;
2249         },
2250
2251         /**
2252          * Returns the actual DOM element
2253          * @method getElement
2254          * @param {String} id the id of the elment to get
2255          * @return {Object} The element
2256          * @deprecated use Roo.getDom instead
2257          * @static
2258          */
2259         getElement: function(id) {
2260             return Roo.getDom(id);
2261         },
2262
2263         /**
2264          * Returns the style property for the DOM element (i.e.,
2265          * document.getElById(id).style)
2266          * @method getCss
2267          * @param {String} id the id of the elment to get
2268          * @return {Object} The style property of the element
2269          * @deprecated use Roo.getDom instead
2270          * @static
2271          */
2272         getCss: function(id) {
2273             var el = Roo.getDom(id);
2274             return (el) ? el.style : null;
2275         },
2276
2277         /**
2278          * Inner class for cached elements
2279          * @class DragDropMgr.ElementWrapper
2280          * @for DragDropMgr
2281          * @private
2282          * @deprecated
2283          */
2284         ElementWrapper: function(el) {
2285                 /**
2286                  * The element
2287                  * @property el
2288                  */
2289                 this.el = el || null;
2290                 /**
2291                  * The element id
2292                  * @property id
2293                  */
2294                 this.id = this.el && el.id;
2295                 /**
2296                  * A reference to the style property
2297                  * @property css
2298                  */
2299                 this.css = this.el && el.style;
2300             },
2301
2302         /**
2303          * Returns the X position of an html element
2304          * @method getPosX
2305          * @param el the element for which to get the position
2306          * @return {int} the X coordinate
2307          * @for DragDropMgr
2308          * @deprecated use Roo.lib.Dom.getX instead
2309          * @static
2310          */
2311         getPosX: function(el) {
2312             return Roo.lib.Dom.getX(el);
2313         },
2314
2315         /**
2316          * Returns the Y position of an html element
2317          * @method getPosY
2318          * @param el the element for which to get the position
2319          * @return {int} the Y coordinate
2320          * @deprecated use Roo.lib.Dom.getY instead
2321          * @static
2322          */
2323         getPosY: function(el) {
2324             return Roo.lib.Dom.getY(el);
2325         },
2326
2327         /**
2328          * Swap two nodes.  In IE, we use the native method, for others we
2329          * emulate the IE behavior
2330          * @method swapNode
2331          * @param n1 the first node to swap
2332          * @param n2 the other node to swap
2333          * @static
2334          */
2335         swapNode: function(n1, n2) {
2336             if (n1.swapNode) {
2337                 n1.swapNode(n2);
2338             } else {
2339                 var p = n2.parentNode;
2340                 var s = n2.nextSibling;
2341
2342                 if (s == n1) {
2343                     p.insertBefore(n1, n2);
2344                 } else if (n2 == n1.nextSibling) {
2345                     p.insertBefore(n2, n1);
2346                 } else {
2347                     n1.parentNode.replaceChild(n2, n1);
2348                     p.insertBefore(n1, s);
2349                 }
2350             }
2351         },
2352
2353         /**
2354          * Returns the current scroll position
2355          * @method getScroll
2356          * @private
2357          * @static
2358          */
2359         getScroll: function () {
2360             var t, l, dde=document.documentElement, db=document.body;
2361             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2362                 t = dde.scrollTop;
2363                 l = dde.scrollLeft;
2364             } else if (db) {
2365                 t = db.scrollTop;
2366                 l = db.scrollLeft;
2367             } else {
2368
2369             }
2370             return { top: t, left: l };
2371         },
2372
2373         /**
2374          * Returns the specified element style property
2375          * @method getStyle
2376          * @param {HTMLElement} el          the element
2377          * @param {string}      styleProp   the style property
2378          * @return {string} The value of the style property
2379          * @deprecated use Roo.lib.Dom.getStyle
2380          * @static
2381          */
2382         getStyle: function(el, styleProp) {
2383             return Roo.fly(el).getStyle(styleProp);
2384         },
2385
2386         /**
2387          * Gets the scrollTop
2388          * @method getScrollTop
2389          * @return {int} the document's scrollTop
2390          * @static
2391          */
2392         getScrollTop: function () { return this.getScroll().top; },
2393
2394         /**
2395          * Gets the scrollLeft
2396          * @method getScrollLeft
2397          * @return {int} the document's scrollTop
2398          * @static
2399          */
2400         getScrollLeft: function () { return this.getScroll().left; },
2401
2402         /**
2403          * Sets the x/y position of an element to the location of the
2404          * target element.
2405          * @method moveToEl
2406          * @param {HTMLElement} moveEl      The element to move
2407          * @param {HTMLElement} targetEl    The position reference element
2408          * @static
2409          */
2410         moveToEl: function (moveEl, targetEl) {
2411             var aCoord = Roo.lib.Dom.getXY(targetEl);
2412             Roo.lib.Dom.setXY(moveEl, aCoord);
2413         },
2414
2415         /**
2416          * Numeric array sort function
2417          * @method numericSort
2418          * @static
2419          */
2420         numericSort: function(a, b) { return (a - b); },
2421
2422         /**
2423          * Internal counter
2424          * @property _timeoutCount
2425          * @private
2426          * @static
2427          */
2428         _timeoutCount: 0,
2429
2430         /**
2431          * Trying to make the load order less important.  Without this we get
2432          * an error if this file is loaded before the Event Utility.
2433          * @method _addListeners
2434          * @private
2435          * @static
2436          */
2437         _addListeners: function() {
2438             var DDM = Roo.dd.DDM;
2439             if ( Roo.lib.Event && document ) {
2440                 DDM._onLoad();
2441             } else {
2442                 if (DDM._timeoutCount > 2000) {
2443                 } else {
2444                     setTimeout(DDM._addListeners, 10);
2445                     if (document && document.body) {
2446                         DDM._timeoutCount += 1;
2447                     }
2448                 }
2449             }
2450         },
2451
2452         /**
2453          * Recursively searches the immediate parent and all child nodes for
2454          * the handle element in order to determine wheter or not it was
2455          * clicked.
2456          * @method handleWasClicked
2457          * @param node the html element to inspect
2458          * @static
2459          */
2460         handleWasClicked: function(node, id) {
2461             if (this.isHandle(id, node.id)) {
2462                 return true;
2463             } else {
2464                 // check to see if this is a text node child of the one we want
2465                 var p = node.parentNode;
2466
2467                 while (p) {
2468                     if (this.isHandle(id, p.id)) {
2469                         return true;
2470                     } else {
2471                         p = p.parentNode;
2472                     }
2473                 }
2474             }
2475
2476             return false;
2477         }
2478
2479     };
2480
2481 }();
2482
2483 // shorter alias, save a few bytes
2484 Roo.dd.DDM = Roo.dd.DragDropMgr;
2485 Roo.dd.DDM._addListeners();
2486
2487 }/*
2488  * Based on:
2489  * Ext JS Library 1.1.1
2490  * Copyright(c) 2006-2007, Ext JS, LLC.
2491  *
2492  * Originally Released Under LGPL - original licence link has changed is not relivant.
2493  *
2494  * Fork - LGPL
2495  * <script type="text/javascript">
2496  */
2497
2498 /**
2499  * @class Roo.dd.DD
2500  * A DragDrop implementation where the linked element follows the
2501  * mouse cursor during a drag.
2502  * @extends Roo.dd.DragDrop
2503  * @constructor
2504  * @param {String} id the id of the linked element
2505  * @param {String} sGroup the group of related DragDrop items
2506  * @param {object} config an object containing configurable attributes
2507  *                Valid properties for DD:
2508  *                    scroll
2509  */
2510 Roo.dd.DD = function(id, sGroup, config) {
2511     if (id) {
2512         this.init(id, sGroup, config);
2513     }
2514 };
2515
2516 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2517
2518     /**
2519      * When set to true, the utility automatically tries to scroll the browser
2520      * window wehn a drag and drop element is dragged near the viewport boundary.
2521      * Defaults to true.
2522      * @property scroll
2523      * @type boolean
2524      */
2525     scroll: true,
2526
2527     /**
2528      * Sets the pointer offset to the distance between the linked element's top
2529      * left corner and the location the element was clicked
2530      * @method autoOffset
2531      * @param {int} iPageX the X coordinate of the click
2532      * @param {int} iPageY the Y coordinate of the click
2533      */
2534     autoOffset: function(iPageX, iPageY) {
2535         var x = iPageX - this.startPageX;
2536         var y = iPageY - this.startPageY;
2537         this.setDelta(x, y);
2538     },
2539
2540     /**
2541      * Sets the pointer offset.  You can call this directly to force the
2542      * offset to be in a particular location (e.g., pass in 0,0 to set it
2543      * to the center of the object)
2544      * @method setDelta
2545      * @param {int} iDeltaX the distance from the left
2546      * @param {int} iDeltaY the distance from the top
2547      */
2548     setDelta: function(iDeltaX, iDeltaY) {
2549         this.deltaX = iDeltaX;
2550         this.deltaY = iDeltaY;
2551     },
2552
2553     /**
2554      * Sets the drag element to the location of the mousedown or click event,
2555      * maintaining the cursor location relative to the location on the element
2556      * that was clicked.  Override this if you want to place the element in a
2557      * location other than where the cursor is.
2558      * @method setDragElPos
2559      * @param {int} iPageX the X coordinate of the mousedown or drag event
2560      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2561      */
2562     setDragElPos: function(iPageX, iPageY) {
2563         // the first time we do this, we are going to check to make sure
2564         // the element has css positioning
2565
2566         var el = this.getDragEl();
2567         this.alignElWithMouse(el, iPageX, iPageY);
2568     },
2569
2570     /**
2571      * Sets the element to the location of the mousedown or click event,
2572      * maintaining the cursor location relative to the location on the element
2573      * that was clicked.  Override this if you want to place the element in a
2574      * location other than where the cursor is.
2575      * @method alignElWithMouse
2576      * @param {HTMLElement} el the element to move
2577      * @param {int} iPageX the X coordinate of the mousedown or drag event
2578      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2579      */
2580     alignElWithMouse: function(el, iPageX, iPageY) {
2581         var oCoord = this.getTargetCoord(iPageX, iPageY);
2582         var fly = el.dom ? el : Roo.fly(el);
2583         if (!this.deltaSetXY) {
2584             var aCoord = [oCoord.x, oCoord.y];
2585             fly.setXY(aCoord);
2586             var newLeft = fly.getLeft(true);
2587             var newTop  = fly.getTop(true);
2588             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2589         } else {
2590             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2591         }
2592
2593         this.cachePosition(oCoord.x, oCoord.y);
2594         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2595         return oCoord;
2596     },
2597
2598     /**
2599      * Saves the most recent position so that we can reset the constraints and
2600      * tick marks on-demand.  We need to know this so that we can calculate the
2601      * number of pixels the element is offset from its original position.
2602      * @method cachePosition
2603      * @param iPageX the current x position (optional, this just makes it so we
2604      * don't have to look it up again)
2605      * @param iPageY the current y position (optional, this just makes it so we
2606      * don't have to look it up again)
2607      */
2608     cachePosition: function(iPageX, iPageY) {
2609         if (iPageX) {
2610             this.lastPageX = iPageX;
2611             this.lastPageY = iPageY;
2612         } else {
2613             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2614             this.lastPageX = aCoord[0];
2615             this.lastPageY = aCoord[1];
2616         }
2617     },
2618
2619     /**
2620      * Auto-scroll the window if the dragged object has been moved beyond the
2621      * visible window boundary.
2622      * @method autoScroll
2623      * @param {int} x the drag element's x position
2624      * @param {int} y the drag element's y position
2625      * @param {int} h the height of the drag element
2626      * @param {int} w the width of the drag element
2627      * @private
2628      */
2629     autoScroll: function(x, y, h, w) {
2630
2631         if (this.scroll) {
2632             // The client height
2633             var clientH = Roo.lib.Dom.getViewWidth();
2634
2635             // The client width
2636             var clientW = Roo.lib.Dom.getViewHeight();
2637
2638             // The amt scrolled down
2639             var st = this.DDM.getScrollTop();
2640
2641             // The amt scrolled right
2642             var sl = this.DDM.getScrollLeft();
2643
2644             // Location of the bottom of the element
2645             var bot = h + y;
2646
2647             // Location of the right of the element
2648             var right = w + x;
2649
2650             // The distance from the cursor to the bottom of the visible area,
2651             // adjusted so that we don't scroll if the cursor is beyond the
2652             // element drag constraints
2653             var toBot = (clientH + st - y - this.deltaY);
2654
2655             // The distance from the cursor to the right of the visible area
2656             var toRight = (clientW + sl - x - this.deltaX);
2657
2658
2659             // How close to the edge the cursor must be before we scroll
2660             // var thresh = (document.all) ? 100 : 40;
2661             var thresh = 40;
2662
2663             // How many pixels to scroll per autoscroll op.  This helps to reduce
2664             // clunky scrolling. IE is more sensitive about this ... it needs this
2665             // value to be higher.
2666             var scrAmt = (document.all) ? 80 : 30;
2667
2668             // Scroll down if we are near the bottom of the visible page and the
2669             // obj extends below the crease
2670             if ( bot > clientH && toBot < thresh ) {
2671                 window.scrollTo(sl, st + scrAmt);
2672             }
2673
2674             // Scroll up if the window is scrolled down and the top of the object
2675             // goes above the top border
2676             if ( y < st && st > 0 && y - st < thresh ) {
2677                 window.scrollTo(sl, st - scrAmt);
2678             }
2679
2680             // Scroll right if the obj is beyond the right border and the cursor is
2681             // near the border.
2682             if ( right > clientW && toRight < thresh ) {
2683                 window.scrollTo(sl + scrAmt, st);
2684             }
2685
2686             // Scroll left if the window has been scrolled to the right and the obj
2687             // extends past the left border
2688             if ( x < sl && sl > 0 && x - sl < thresh ) {
2689                 window.scrollTo(sl - scrAmt, st);
2690             }
2691         }
2692     },
2693
2694     /**
2695      * Finds the location the element should be placed if we want to move
2696      * it to where the mouse location less the click offset would place us.
2697      * @method getTargetCoord
2698      * @param {int} iPageX the X coordinate of the click
2699      * @param {int} iPageY the Y coordinate of the click
2700      * @return an object that contains the coordinates (Object.x and Object.y)
2701      * @private
2702      */
2703     getTargetCoord: function(iPageX, iPageY) {
2704
2705
2706         var x = iPageX - this.deltaX;
2707         var y = iPageY - this.deltaY;
2708
2709         if (this.constrainX) {
2710             if (x < this.minX) { x = this.minX; }
2711             if (x > this.maxX) { x = this.maxX; }
2712         }
2713
2714         if (this.constrainY) {
2715             if (y < this.minY) { y = this.minY; }
2716             if (y > this.maxY) { y = this.maxY; }
2717         }
2718
2719         x = this.getTick(x, this.xTicks);
2720         y = this.getTick(y, this.yTicks);
2721
2722
2723         return {x:x, y:y};
2724     },
2725
2726     /*
2727      * Sets up config options specific to this class. Overrides
2728      * Roo.dd.DragDrop, but all versions of this method through the
2729      * inheritance chain are called
2730      */
2731     applyConfig: function() {
2732         Roo.dd.DD.superclass.applyConfig.call(this);
2733         this.scroll = (this.config.scroll !== false);
2734     },
2735
2736     /*
2737      * Event that fires prior to the onMouseDown event.  Overrides
2738      * Roo.dd.DragDrop.
2739      */
2740     b4MouseDown: function(e) {
2741         // this.resetConstraints();
2742         this.autoOffset(e.getPageX(),
2743                             e.getPageY());
2744     },
2745
2746     /*
2747      * Event that fires prior to the onDrag event.  Overrides
2748      * Roo.dd.DragDrop.
2749      */
2750     b4Drag: function(e) {
2751         this.setDragElPos(e.getPageX(),
2752                             e.getPageY());
2753     },
2754
2755     toString: function() {
2756         return ("DD " + this.id);
2757     }
2758
2759     //////////////////////////////////////////////////////////////////////////
2760     // Debugging ygDragDrop events that can be overridden
2761     //////////////////////////////////////////////////////////////////////////
2762     /*
2763     startDrag: function(x, y) {
2764     },
2765
2766     onDrag: function(e) {
2767     },
2768
2769     onDragEnter: function(e, id) {
2770     },
2771
2772     onDragOver: function(e, id) {
2773     },
2774
2775     onDragOut: function(e, id) {
2776     },
2777
2778     onDragDrop: function(e, id) {
2779     },
2780
2781     endDrag: function(e) {
2782     }
2783
2784     */
2785
2786 });/*
2787  * Based on:
2788  * Ext JS Library 1.1.1
2789  * Copyright(c) 2006-2007, Ext JS, LLC.
2790  *
2791  * Originally Released Under LGPL - original licence link has changed is not relivant.
2792  *
2793  * Fork - LGPL
2794  * <script type="text/javascript">
2795  */
2796
2797 /**
2798  * @class Roo.dd.DDProxy
2799  * A DragDrop implementation that inserts an empty, bordered div into
2800  * the document that follows the cursor during drag operations.  At the time of
2801  * the click, the frame div is resized to the dimensions of the linked html
2802  * element, and moved to the exact location of the linked element.
2803  *
2804  * References to the "frame" element refer to the single proxy element that
2805  * was created to be dragged in place of all DDProxy elements on the
2806  * page.
2807  *
2808  * @extends Roo.dd.DD
2809  * @constructor
2810  * @param {String} id the id of the linked html element
2811  * @param {String} sGroup the group of related DragDrop objects
2812  * @param {object} config an object containing configurable attributes
2813  *                Valid properties for DDProxy in addition to those in DragDrop:
2814  *                   resizeFrame, centerFrame, dragElId
2815  */
2816 Roo.dd.DDProxy = function(id, sGroup, config) {
2817     if (id) {
2818         this.init(id, sGroup, config);
2819         this.initFrame();
2820     }
2821 };
2822
2823 /**
2824  * The default drag frame div id
2825  * @property Roo.dd.DDProxy.dragElId
2826  * @type String
2827  * @static
2828  */
2829 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2830
2831 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2832
2833     /**
2834      * By default we resize the drag frame to be the same size as the element
2835      * we want to drag (this is to get the frame effect).  We can turn it off
2836      * if we want a different behavior.
2837      * @property resizeFrame
2838      * @type boolean
2839      */
2840     resizeFrame: true,
2841
2842     /**
2843      * By default the frame is positioned exactly where the drag element is, so
2844      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2845      * you do not have constraints on the obj is to have the drag frame centered
2846      * around the cursor.  Set centerFrame to true for this effect.
2847      * @property centerFrame
2848      * @type boolean
2849      */
2850     centerFrame: false,
2851
2852     /**
2853      * Creates the proxy element if it does not yet exist
2854      * @method createFrame
2855      */
2856     createFrame: function() {
2857         var self = this;
2858         var body = document.body;
2859
2860         if (!body || !body.firstChild) {
2861             setTimeout( function() { self.createFrame(); }, 50 );
2862             return;
2863         }
2864
2865         var div = this.getDragEl();
2866
2867         if (!div) {
2868             div    = document.createElement("div");
2869             div.id = this.dragElId;
2870             var s  = div.style;
2871
2872             s.position   = "absolute";
2873             s.visibility = "hidden";
2874             s.cursor     = "move";
2875             s.border     = "2px solid #aaa";
2876             s.zIndex     = 999;
2877
2878             // appendChild can blow up IE if invoked prior to the window load event
2879             // while rendering a table.  It is possible there are other scenarios
2880             // that would cause this to happen as well.
2881             body.insertBefore(div, body.firstChild);
2882         }
2883     },
2884
2885     /**
2886      * Initialization for the drag frame element.  Must be called in the
2887      * constructor of all subclasses
2888      * @method initFrame
2889      */
2890     initFrame: function() {
2891         this.createFrame();
2892     },
2893
2894     applyConfig: function() {
2895         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2896
2897         this.resizeFrame = (this.config.resizeFrame !== false);
2898         this.centerFrame = (this.config.centerFrame);
2899         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2900     },
2901
2902     /**
2903      * Resizes the drag frame to the dimensions of the clicked object, positions
2904      * it over the object, and finally displays it
2905      * @method showFrame
2906      * @param {int} iPageX X click position
2907      * @param {int} iPageY Y click position
2908      * @private
2909      */
2910     showFrame: function(iPageX, iPageY) {
2911         var el = this.getEl();
2912         var dragEl = this.getDragEl();
2913         var s = dragEl.style;
2914
2915         this._resizeProxy();
2916
2917         if (this.centerFrame) {
2918             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2919                            Math.round(parseInt(s.height, 10)/2) );
2920         }
2921
2922         this.setDragElPos(iPageX, iPageY);
2923
2924         Roo.fly(dragEl).show();
2925     },
2926
2927     /**
2928      * The proxy is automatically resized to the dimensions of the linked
2929      * element when a drag is initiated, unless resizeFrame is set to false
2930      * @method _resizeProxy
2931      * @private
2932      */
2933     _resizeProxy: function() {
2934         if (this.resizeFrame) {
2935             var el = this.getEl();
2936             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2937         }
2938     },
2939
2940     // overrides Roo.dd.DragDrop
2941     b4MouseDown: function(e) {
2942         var x = e.getPageX();
2943         var y = e.getPageY();
2944         this.autoOffset(x, y);
2945         this.setDragElPos(x, y);
2946     },
2947
2948     // overrides Roo.dd.DragDrop
2949     b4StartDrag: function(x, y) {
2950         // show the drag frame
2951         this.showFrame(x, y);
2952     },
2953
2954     // overrides Roo.dd.DragDrop
2955     b4EndDrag: function(e) {
2956         Roo.fly(this.getDragEl()).hide();
2957     },
2958
2959     // overrides Roo.dd.DragDrop
2960     // By default we try to move the element to the last location of the frame.
2961     // This is so that the default behavior mirrors that of Roo.dd.DD.
2962     endDrag: function(e) {
2963
2964         var lel = this.getEl();
2965         var del = this.getDragEl();
2966
2967         // Show the drag frame briefly so we can get its position
2968         del.style.visibility = "";
2969
2970         this.beforeMove();
2971         // Hide the linked element before the move to get around a Safari
2972         // rendering bug.
2973         lel.style.visibility = "hidden";
2974         Roo.dd.DDM.moveToEl(lel, del);
2975         del.style.visibility = "hidden";
2976         lel.style.visibility = "";
2977
2978         this.afterDrag();
2979     },
2980
2981     beforeMove : function(){
2982
2983     },
2984
2985     afterDrag : function(){
2986
2987     },
2988
2989     toString: function() {
2990         return ("DDProxy " + this.id);
2991     }
2992
2993 });
2994 /*
2995  * Based on:
2996  * Ext JS Library 1.1.1
2997  * Copyright(c) 2006-2007, Ext JS, LLC.
2998  *
2999  * Originally Released Under LGPL - original licence link has changed is not relivant.
3000  *
3001  * Fork - LGPL
3002  * <script type="text/javascript">
3003  */
3004
3005  /**
3006  * @class Roo.dd.DDTarget
3007  * A DragDrop implementation that does not move, but can be a drop
3008  * target.  You would get the same result by simply omitting implementation
3009  * for the event callbacks, but this way we reduce the processing cost of the
3010  * event listener and the callbacks.
3011  * @extends Roo.dd.DragDrop
3012  * @constructor
3013  * @param {String} id the id of the element that is a drop target
3014  * @param {String} sGroup the group of related DragDrop objects
3015  * @param {object} config an object containing configurable attributes
3016  *                 Valid properties for DDTarget in addition to those in
3017  *                 DragDrop:
3018  *                    none
3019  */
3020 Roo.dd.DDTarget = function(id, sGroup, config) {
3021     if (id) {
3022         this.initTarget(id, sGroup, config);
3023     }
3024     if (config.listeners || config.events) { 
3025        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
3026             listeners : config.listeners || {}, 
3027             events : config.events || {} 
3028         });    
3029     }
3030 };
3031
3032 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3033 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3034     toString: function() {
3035         return ("DDTarget " + this.id);
3036     }
3037 });
3038 /*
3039  * Based on:
3040  * Ext JS Library 1.1.1
3041  * Copyright(c) 2006-2007, Ext JS, LLC.
3042  *
3043  * Originally Released Under LGPL - original licence link has changed is not relivant.
3044  *
3045  * Fork - LGPL
3046  * <script type="text/javascript">
3047  */
3048  
3049
3050 /**
3051  * @class Roo.dd.ScrollManager
3052  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3053  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3054  * @singleton
3055  */
3056 Roo.dd.ScrollManager = function(){
3057     var ddm = Roo.dd.DragDropMgr;
3058     var els = {};
3059     var dragEl = null;
3060     var proc = {};
3061     
3062     
3063     
3064     var onStop = function(e){
3065         dragEl = null;
3066         clearProc();
3067     };
3068     
3069     var triggerRefresh = function(){
3070         if(ddm.dragCurrent){
3071              ddm.refreshCache(ddm.dragCurrent.groups);
3072         }
3073     };
3074     
3075     var doScroll = function(){
3076         if(ddm.dragCurrent){
3077             var dds = Roo.dd.ScrollManager;
3078             if(!dds.animate){
3079                 if(proc.el.scroll(proc.dir, dds.increment)){
3080                     triggerRefresh();
3081                 }
3082             }else{
3083                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3084             }
3085         }
3086     };
3087     
3088     var clearProc = function(){
3089         if(proc.id){
3090             clearInterval(proc.id);
3091         }
3092         proc.id = 0;
3093         proc.el = null;
3094         proc.dir = "";
3095     };
3096     
3097     var startProc = function(el, dir){
3098          Roo.log('scroll startproc');
3099         clearProc();
3100         proc.el = el;
3101         proc.dir = dir;
3102         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3103     };
3104     
3105     var onFire = function(e, isDrop){
3106        
3107         if(isDrop || !ddm.dragCurrent){ return; }
3108         var dds = Roo.dd.ScrollManager;
3109         if(!dragEl || dragEl != ddm.dragCurrent){
3110             dragEl = ddm.dragCurrent;
3111             // refresh regions on drag start
3112             dds.refreshCache();
3113         }
3114         
3115         var xy = Roo.lib.Event.getXY(e);
3116         var pt = new Roo.lib.Point(xy[0], xy[1]);
3117         for(var id in els){
3118             var el = els[id], r = el._region;
3119             if(r && r.contains(pt) && el.isScrollable()){
3120                 if(r.bottom - pt.y <= dds.thresh){
3121                     if(proc.el != el){
3122                         startProc(el, "down");
3123                     }
3124                     return;
3125                 }else if(r.right - pt.x <= dds.thresh){
3126                     if(proc.el != el){
3127                         startProc(el, "left");
3128                     }
3129                     return;
3130                 }else if(pt.y - r.top <= dds.thresh){
3131                     if(proc.el != el){
3132                         startProc(el, "up");
3133                     }
3134                     return;
3135                 }else if(pt.x - r.left <= dds.thresh){
3136                     if(proc.el != el){
3137                         startProc(el, "right");
3138                     }
3139                     return;
3140                 }
3141             }
3142         }
3143         clearProc();
3144     };
3145     
3146     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3147     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3148     
3149     return {
3150         /**
3151          * Registers new overflow element(s) to auto scroll
3152          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3153          */
3154         register : function(el){
3155             if(el instanceof Array){
3156                 for(var i = 0, len = el.length; i < len; i++) {
3157                         this.register(el[i]);
3158                 }
3159             }else{
3160                 el = Roo.get(el);
3161                 els[el.id] = el;
3162             }
3163             Roo.dd.ScrollManager.els = els;
3164         },
3165         
3166         /**
3167          * Unregisters overflow element(s) so they are no longer scrolled
3168          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3169          */
3170         unregister : function(el){
3171             if(el instanceof Array){
3172                 for(var i = 0, len = el.length; i < len; i++) {
3173                         this.unregister(el[i]);
3174                 }
3175             }else{
3176                 el = Roo.get(el);
3177                 delete els[el.id];
3178             }
3179         },
3180         
3181         /**
3182          * The number of pixels from the edge of a container the pointer needs to be to 
3183          * trigger scrolling (defaults to 25)
3184          * @type Number
3185          */
3186         thresh : 25,
3187         
3188         /**
3189          * The number of pixels to scroll in each scroll increment (defaults to 50)
3190          * @type Number
3191          */
3192         increment : 100,
3193         
3194         /**
3195          * The frequency of scrolls in milliseconds (defaults to 500)
3196          * @type Number
3197          */
3198         frequency : 500,
3199         
3200         /**
3201          * True to animate the scroll (defaults to true)
3202          * @type Boolean
3203          */
3204         animate: true,
3205         
3206         /**
3207          * The animation duration in seconds - 
3208          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3209          * @type Number
3210          */
3211         animDuration: .4,
3212         
3213         /**
3214          * Manually trigger a cache refresh.
3215          */
3216         refreshCache : function(){
3217             for(var id in els){
3218                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3219                     els[id]._region = els[id].getRegion();
3220                 }
3221             }
3222         }
3223     };
3224 }();/*
3225  * Based on:
3226  * Ext JS Library 1.1.1
3227  * Copyright(c) 2006-2007, Ext JS, LLC.
3228  *
3229  * Originally Released Under LGPL - original licence link has changed is not relivant.
3230  *
3231  * Fork - LGPL
3232  * <script type="text/javascript">
3233  */
3234  
3235
3236 /**
3237  * @class Roo.dd.Registry
3238  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3239  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3240  * @singleton
3241  */
3242 Roo.dd.Registry = function(){
3243     var elements = {}; 
3244     var handles = {}; 
3245     var autoIdSeed = 0;
3246
3247     var getId = function(el, autogen){
3248         if(typeof el == "string"){
3249             return el;
3250         }
3251         var id = el.id;
3252         if(!id && autogen !== false){
3253             id = "roodd-" + (++autoIdSeed);
3254             el.id = id;
3255         }
3256         return id;
3257     };
3258     
3259     return {
3260     /**
3261      * Register a drag drop element
3262      * @param {String|HTMLElement} element The id or DOM node to register
3263      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3264      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3265      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3266      * populated in the data object (if applicable):
3267      * <pre>
3268 Value      Description<br />
3269 ---------  ------------------------------------------<br />
3270 handles    Array of DOM nodes that trigger dragging<br />
3271            for the element being registered<br />
3272 isHandle   True if the element passed in triggers<br />
3273            dragging itself, else false
3274 </pre>
3275      */
3276         register : function(el, data){
3277             data = data || {};
3278             if(typeof el == "string"){
3279                 el = document.getElementById(el);
3280             }
3281             data.ddel = el;
3282             elements[getId(el)] = data;
3283             if(data.isHandle !== false){
3284                 handles[data.ddel.id] = data;
3285             }
3286             if(data.handles){
3287                 var hs = data.handles;
3288                 for(var i = 0, len = hs.length; i < len; i++){
3289                         handles[getId(hs[i])] = data;
3290                 }
3291             }
3292         },
3293
3294     /**
3295      * Unregister a drag drop element
3296      * @param {String|HTMLElement}  element The id or DOM node to unregister
3297      */
3298         unregister : function(el){
3299             var id = getId(el, false);
3300             var data = elements[id];
3301             if(data){
3302                 delete elements[id];
3303                 if(data.handles){
3304                     var hs = data.handles;
3305                     for(var i = 0, len = hs.length; i < len; i++){
3306                         delete handles[getId(hs[i], false)];
3307                     }
3308                 }
3309             }
3310         },
3311
3312     /**
3313      * Returns the handle registered for a DOM Node by id
3314      * @param {String|HTMLElement} id The DOM node or id to look up
3315      * @return {Object} handle The custom handle data
3316      */
3317         getHandle : function(id){
3318             if(typeof id != "string"){ // must be element?
3319                 id = id.id;
3320             }
3321             return handles[id];
3322         },
3323
3324     /**
3325      * Returns the handle that is registered for the DOM node that is the target of the event
3326      * @param {Event} e The event
3327      * @return {Object} handle The custom handle data
3328      */
3329         getHandleFromEvent : function(e){
3330             var t = Roo.lib.Event.getTarget(e);
3331             return t ? handles[t.id] : null;
3332         },
3333
3334     /**
3335      * Returns a custom data object that is registered for a DOM node by id
3336      * @param {String|HTMLElement} id The DOM node or id to look up
3337      * @return {Object} data The custom data
3338      */
3339         getTarget : function(id){
3340             if(typeof id != "string"){ // must be element?
3341                 id = id.id;
3342             }
3343             return elements[id];
3344         },
3345
3346     /**
3347      * Returns a custom data object that is registered for the DOM node that is the target of the event
3348      * @param {Event} e The event
3349      * @return {Object} data The custom data
3350      */
3351         getTargetFromEvent : function(e){
3352             var t = Roo.lib.Event.getTarget(e);
3353             return t ? elements[t.id] || handles[t.id] : null;
3354         }
3355     };
3356 }();/*
3357  * Based on:
3358  * Ext JS Library 1.1.1
3359  * Copyright(c) 2006-2007, Ext JS, LLC.
3360  *
3361  * Originally Released Under LGPL - original licence link has changed is not relivant.
3362  *
3363  * Fork - LGPL
3364  * <script type="text/javascript">
3365  */
3366  
3367
3368 /**
3369  * @class Roo.dd.StatusProxy
3370  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3371  * default drag proxy used by all Roo.dd components.
3372  * @constructor
3373  * @param {Object} config
3374  */
3375 Roo.dd.StatusProxy = function(config){
3376     Roo.apply(this, config);
3377     this.id = this.id || Roo.id();
3378     this.el = new Roo.Layer({
3379         dh: {
3380             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3381                 {tag: "div", cls: "x-dd-drop-icon"},
3382                 {tag: "div", cls: "x-dd-drag-ghost"}
3383             ]
3384         }, 
3385         shadow: !config || config.shadow !== false
3386     });
3387     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3388     this.dropStatus = this.dropNotAllowed;
3389 };
3390
3391 Roo.dd.StatusProxy.prototype = {
3392     /**
3393      * @cfg {String} dropAllowed
3394      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3395      */
3396     dropAllowed : "x-dd-drop-ok",
3397     /**
3398      * @cfg {String} dropNotAllowed
3399      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3400      */
3401     dropNotAllowed : "x-dd-drop-nodrop",
3402
3403     /**
3404      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3405      * over the current target element.
3406      * @param {String} cssClass The css class for the new drop status indicator image
3407      */
3408     setStatus : function(cssClass){
3409         cssClass = cssClass || this.dropNotAllowed;
3410         if(this.dropStatus != cssClass){
3411             this.el.replaceClass(this.dropStatus, cssClass);
3412             this.dropStatus = cssClass;
3413         }
3414     },
3415
3416     /**
3417      * Resets the status indicator to the default dropNotAllowed value
3418      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3419      */
3420     reset : function(clearGhost){
3421         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3422         this.dropStatus = this.dropNotAllowed;
3423         if(clearGhost){
3424             this.ghost.update("");
3425         }
3426     },
3427
3428     /**
3429      * Updates the contents of the ghost element
3430      * @param {String} html The html that will replace the current innerHTML of the ghost element
3431      */
3432     update : function(html){
3433         if(typeof html == "string"){
3434             this.ghost.update(html);
3435         }else{
3436             this.ghost.update("");
3437             html.style.margin = "0";
3438             this.ghost.dom.appendChild(html);
3439         }
3440         // ensure float = none set?? cant remember why though.
3441         var el = this.ghost.dom.firstChild;
3442                 if(el){
3443                         Roo.fly(el).setStyle('float', 'none');
3444                 }
3445     },
3446     
3447     /**
3448      * Returns the underlying proxy {@link Roo.Layer}
3449      * @return {Roo.Layer} el
3450     */
3451     getEl : function(){
3452         return this.el;
3453     },
3454
3455     /**
3456      * Returns the ghost element
3457      * @return {Roo.Element} el
3458      */
3459     getGhost : function(){
3460         return this.ghost;
3461     },
3462
3463     /**
3464      * Hides the proxy
3465      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3466      */
3467     hide : function(clear){
3468         this.el.hide();
3469         if(clear){
3470             this.reset(true);
3471         }
3472     },
3473
3474     /**
3475      * Stops the repair animation if it's currently running
3476      */
3477     stop : function(){
3478         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3479             this.anim.stop();
3480         }
3481     },
3482
3483     /**
3484      * Displays this proxy
3485      */
3486     show : function(){
3487         this.el.show();
3488     },
3489
3490     /**
3491      * Force the Layer to sync its shadow and shim positions to the element
3492      */
3493     sync : function(){
3494         this.el.sync();
3495     },
3496
3497     /**
3498      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3499      * invalid drop operation by the item being dragged.
3500      * @param {Array} xy The XY position of the element ([x, y])
3501      * @param {Function} callback The function to call after the repair is complete
3502      * @param {Object} scope The scope in which to execute the callback
3503      */
3504     repair : function(xy, callback, scope){
3505         this.callback = callback;
3506         this.scope = scope;
3507         if(xy && this.animRepair !== false){
3508             this.el.addClass("x-dd-drag-repair");
3509             this.el.hideUnders(true);
3510             this.anim = this.el.shift({
3511                 duration: this.repairDuration || .5,
3512                 easing: 'easeOut',
3513                 xy: xy,
3514                 stopFx: true,
3515                 callback: this.afterRepair,
3516                 scope: this
3517             });
3518         }else{
3519             this.afterRepair();
3520         }
3521     },
3522
3523     // private
3524     afterRepair : function(){
3525         this.hide(true);
3526         if(typeof this.callback == "function"){
3527             this.callback.call(this.scope || this);
3528         }
3529         this.callback = null;
3530         this.scope = null;
3531     }
3532 };/*
3533  * Based on:
3534  * Ext JS Library 1.1.1
3535  * Copyright(c) 2006-2007, Ext JS, LLC.
3536  *
3537  * Originally Released Under LGPL - original licence link has changed is not relivant.
3538  *
3539  * Fork - LGPL
3540  * <script type="text/javascript">
3541  */
3542
3543 /**
3544  * @class Roo.dd.DragSource
3545  * @extends Roo.dd.DDProxy
3546  * A simple class that provides the basic implementation needed to make any element draggable.
3547  * @constructor
3548  * @param {String/HTMLElement/Element} el The container element
3549  * @param {Object} config
3550  */
3551 Roo.dd.DragSource = function(el, config){
3552     this.el = Roo.get(el);
3553     this.dragData = {};
3554     
3555     Roo.apply(this, config);
3556     
3557     if(!this.proxy){
3558         this.proxy = new Roo.dd.StatusProxy();
3559     }
3560
3561     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3562           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3563     
3564     this.dragging = false;
3565 };
3566
3567 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3568     /**
3569      * @cfg {String} dropAllowed
3570      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3571      */
3572     dropAllowed : "x-dd-drop-ok",
3573     /**
3574      * @cfg {String} dropNotAllowed
3575      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3576      */
3577     dropNotAllowed : "x-dd-drop-nodrop",
3578
3579     /**
3580      * Returns the data object associated with this drag source
3581      * @return {Object} data An object containing arbitrary data
3582      */
3583     getDragData : function(e){
3584         return this.dragData;
3585     },
3586
3587     // private
3588     onDragEnter : function(e, id){
3589         var target = Roo.dd.DragDropMgr.getDDById(id);
3590         this.cachedTarget = target;
3591         if(this.beforeDragEnter(target, e, id) !== false){
3592             if(target.isNotifyTarget){
3593                 var status = target.notifyEnter(this, e, this.dragData);
3594                 this.proxy.setStatus(status);
3595             }else{
3596                 this.proxy.setStatus(this.dropAllowed);
3597             }
3598             
3599             if(this.afterDragEnter){
3600                 /**
3601                  * An empty function by default, but provided so that you can perform a custom action
3602                  * when the dragged item enters the drop target by providing an implementation.
3603                  * @param {Roo.dd.DragDrop} target The drop target
3604                  * @param {Event} e The event object
3605                  * @param {String} id The id of the dragged element
3606                  * @method afterDragEnter
3607                  */
3608                 this.afterDragEnter(target, e, id);
3609             }
3610         }
3611     },
3612
3613     /**
3614      * An empty function by default, but provided so that you can perform a custom action
3615      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3616      * @param {Roo.dd.DragDrop} target The drop target
3617      * @param {Event} e The event object
3618      * @param {String} id The id of the dragged element
3619      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3620      */
3621     beforeDragEnter : function(target, e, id){
3622         return true;
3623     },
3624
3625     // private
3626     alignElWithMouse: function() {
3627         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3628         this.proxy.sync();
3629     },
3630
3631     // private
3632     onDragOver : function(e, id){
3633         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3634         if(this.beforeDragOver(target, e, id) !== false){
3635             if(target.isNotifyTarget){
3636                 var status = target.notifyOver(this, e, this.dragData);
3637                 this.proxy.setStatus(status);
3638             }
3639
3640             if(this.afterDragOver){
3641                 /**
3642                  * An empty function by default, but provided so that you can perform a custom action
3643                  * while the dragged item is over the drop target by providing an implementation.
3644                  * @param {Roo.dd.DragDrop} target The drop target
3645                  * @param {Event} e The event object
3646                  * @param {String} id The id of the dragged element
3647                  * @method afterDragOver
3648                  */
3649                 this.afterDragOver(target, e, id);
3650             }
3651         }
3652     },
3653
3654     /**
3655      * An empty function by default, but provided so that you can perform a custom action
3656      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3657      * @param {Roo.dd.DragDrop} target The drop target
3658      * @param {Event} e The event object
3659      * @param {String} id The id of the dragged element
3660      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3661      */
3662     beforeDragOver : function(target, e, id){
3663         return true;
3664     },
3665
3666     // private
3667     onDragOut : function(e, id){
3668         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3669         if(this.beforeDragOut(target, e, id) !== false){
3670             if(target.isNotifyTarget){
3671                 target.notifyOut(this, e, this.dragData);
3672             }
3673             this.proxy.reset();
3674             if(this.afterDragOut){
3675                 /**
3676                  * An empty function by default, but provided so that you can perform a custom action
3677                  * after the dragged item is dragged out of the target without dropping.
3678                  * @param {Roo.dd.DragDrop} target The drop target
3679                  * @param {Event} e The event object
3680                  * @param {String} id The id of the dragged element
3681                  * @method afterDragOut
3682                  */
3683                 this.afterDragOut(target, e, id);
3684             }
3685         }
3686         this.cachedTarget = null;
3687     },
3688
3689     /**
3690      * An empty function by default, but provided so that you can perform a custom action before the dragged
3691      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3692      * @param {Roo.dd.DragDrop} target The drop target
3693      * @param {Event} e The event object
3694      * @param {String} id The id of the dragged element
3695      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3696      */
3697     beforeDragOut : function(target, e, id){
3698         return true;
3699     },
3700     
3701     // private
3702     onDragDrop : function(e, id){
3703         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3704         if(this.beforeDragDrop(target, e, id) !== false){
3705             if(target.isNotifyTarget){
3706                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3707                     this.onValidDrop(target, e, id);
3708                 }else{
3709                     this.onInvalidDrop(target, e, id);
3710                 }
3711             }else{
3712                 this.onValidDrop(target, e, id);
3713             }
3714             
3715             if(this.afterDragDrop){
3716                 /**
3717                  * An empty function by default, but provided so that you can perform a custom action
3718                  * after a valid drag drop has occurred by providing an implementation.
3719                  * @param {Roo.dd.DragDrop} target The drop target
3720                  * @param {Event} e The event object
3721                  * @param {String} id The id of the dropped element
3722                  * @method afterDragDrop
3723                  */
3724                 this.afterDragDrop(target, e, id);
3725             }
3726         }
3727         delete this.cachedTarget;
3728     },
3729
3730     /**
3731      * An empty function by default, but provided so that you can perform a custom action before the dragged
3732      * item is dropped onto the target and optionally cancel the onDragDrop.
3733      * @param {Roo.dd.DragDrop} target The drop target
3734      * @param {Event} e The event object
3735      * @param {String} id The id of the dragged element
3736      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3737      */
3738     beforeDragDrop : function(target, e, id){
3739         return true;
3740     },
3741
3742     // private
3743     onValidDrop : function(target, e, id){
3744         this.hideProxy();
3745         if(this.afterValidDrop){
3746             /**
3747              * An empty function by default, but provided so that you can perform a custom action
3748              * after a valid drop has occurred by providing an implementation.
3749              * @param {Object} target The target DD 
3750              * @param {Event} e The event object
3751              * @param {String} id The id of the dropped element
3752              * @method afterInvalidDrop
3753              */
3754             this.afterValidDrop(target, e, id);
3755         }
3756     },
3757
3758     // private
3759     getRepairXY : function(e, data){
3760         return this.el.getXY();  
3761     },
3762
3763     // private
3764     onInvalidDrop : function(target, e, id){
3765         this.beforeInvalidDrop(target, e, id);
3766         if(this.cachedTarget){
3767             if(this.cachedTarget.isNotifyTarget){
3768                 this.cachedTarget.notifyOut(this, e, this.dragData);
3769             }
3770             this.cacheTarget = null;
3771         }
3772         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3773
3774         if(this.afterInvalidDrop){
3775             /**
3776              * An empty function by default, but provided so that you can perform a custom action
3777              * after an invalid drop has occurred by providing an implementation.
3778              * @param {Event} e The event object
3779              * @param {String} id The id of the dropped element
3780              * @method afterInvalidDrop
3781              */
3782             this.afterInvalidDrop(e, id);
3783         }
3784     },
3785
3786     // private
3787     afterRepair : function(){
3788         if(Roo.enableFx){
3789             this.el.highlight(this.hlColor || "c3daf9");
3790         }
3791         this.dragging = false;
3792     },
3793
3794     /**
3795      * An empty function by default, but provided so that you can perform a custom action after an invalid
3796      * drop has occurred.
3797      * @param {Roo.dd.DragDrop} target The drop target
3798      * @param {Event} e The event object
3799      * @param {String} id The id of the dragged element
3800      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3801      */
3802     beforeInvalidDrop : function(target, e, id){
3803         return true;
3804     },
3805
3806     // private
3807     handleMouseDown : function(e){
3808         if(this.dragging) {
3809             return;
3810         }
3811         var data = this.getDragData(e);
3812         if(data && this.onBeforeDrag(data, e) !== false){
3813             this.dragData = data;
3814             this.proxy.stop();
3815             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3816         } 
3817     },
3818
3819     /**
3820      * An empty function by default, but provided so that you can perform a custom action before the initial
3821      * drag event begins and optionally cancel it.
3822      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3823      * @param {Event} e The event object
3824      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3825      */
3826     onBeforeDrag : function(data, e){
3827         return true;
3828     },
3829
3830     /**
3831      * An empty function by default, but provided so that you can perform a custom action once the initial
3832      * drag event has begun.  The drag cannot be canceled from this function.
3833      * @param {Number} x The x position of the click on the dragged object
3834      * @param {Number} y The y position of the click on the dragged object
3835      */
3836     onStartDrag : Roo.emptyFn,
3837
3838     // private - YUI override
3839     startDrag : function(x, y){
3840         this.proxy.reset();
3841         this.dragging = true;
3842         this.proxy.update("");
3843         this.onInitDrag(x, y);
3844         this.proxy.show();
3845     },
3846
3847     // private
3848     onInitDrag : function(x, y){
3849         var clone = this.el.dom.cloneNode(true);
3850         clone.id = Roo.id(); // prevent duplicate ids
3851         this.proxy.update(clone);
3852         this.onStartDrag(x, y);
3853         return true;
3854     },
3855
3856     /**
3857      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3858      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3859      */
3860     getProxy : function(){
3861         return this.proxy;  
3862     },
3863
3864     /**
3865      * Hides the drag source's {@link Roo.dd.StatusProxy}
3866      */
3867     hideProxy : function(){
3868         this.proxy.hide();  
3869         this.proxy.reset(true);
3870         this.dragging = false;
3871     },
3872
3873     // private
3874     triggerCacheRefresh : function(){
3875         Roo.dd.DDM.refreshCache(this.groups);
3876     },
3877
3878     // private - override to prevent hiding
3879     b4EndDrag: function(e) {
3880     },
3881
3882     // private - override to prevent moving
3883     endDrag : function(e){
3884         this.onEndDrag(this.dragData, e);
3885     },
3886
3887     // private
3888     onEndDrag : function(data, e){
3889     },
3890     
3891     // private - pin to cursor
3892     autoOffset : function(x, y) {
3893         this.setDelta(-12, -20);
3894     }    
3895 });/*
3896  * Based on:
3897  * Ext JS Library 1.1.1
3898  * Copyright(c) 2006-2007, Ext JS, LLC.
3899  *
3900  * Originally Released Under LGPL - original licence link has changed is not relivant.
3901  *
3902  * Fork - LGPL
3903  * <script type="text/javascript">
3904  */
3905
3906
3907 /**
3908  * @class Roo.dd.DropTarget
3909  * @extends Roo.dd.DDTarget
3910  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3911  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3912  * @constructor
3913  * @param {String/HTMLElement/Element} el The container element
3914  * @param {Object} config
3915  */
3916 Roo.dd.DropTarget = function(el, config){
3917     this.el = Roo.get(el);
3918     
3919     var listeners = false; ;
3920     if (config && config.listeners) {
3921         listeners= config.listeners;
3922         delete config.listeners;
3923     }
3924     Roo.apply(this, config);
3925     
3926     if(this.containerScroll){
3927         Roo.dd.ScrollManager.register(this.el);
3928     }
3929     this.addEvents( {
3930          /**
3931          * @scope Roo.dd.DropTarget
3932          */
3933          
3934          /**
3935          * @event enter
3936          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3937          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3938          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3939          * 
3940          * IMPORTANT : it should set this.overClass and this.dropAllowed
3941          * 
3942          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3943          * @param {Event} e The event
3944          * @param {Object} data An object containing arbitrary data supplied by the drag source
3945          */
3946         "enter" : true,
3947         
3948          /**
3949          * @event over
3950          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3951          * This method will be called on every mouse movement while the drag source is over the drop target.
3952          * This default implementation simply returns the dropAllowed config value.
3953          * 
3954          * IMPORTANT : it should set this.dropAllowed
3955          * 
3956          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3957          * @param {Event} e The event
3958          * @param {Object} data An object containing arbitrary data supplied by the drag source
3959          
3960          */
3961         "over" : true,
3962         /**
3963          * @event out
3964          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3965          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3966          * overClass (if any) from the drop element.
3967          * 
3968          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3969          * @param {Event} e The event
3970          * @param {Object} data An object containing arbitrary data supplied by the drag source
3971          */
3972          "out" : true,
3973          
3974         /**
3975          * @event drop
3976          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3977          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3978          * implementation that does something to process the drop event and returns true so that the drag source's
3979          * repair action does not run.
3980          * 
3981          * IMPORTANT : it should set this.success
3982          * 
3983          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3984          * @param {Event} e The event
3985          * @param {Object} data An object containing arbitrary data supplied by the drag source
3986         */
3987          "drop" : true
3988     });
3989             
3990      
3991     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3992         this.el.dom, 
3993         this.ddGroup || this.group,
3994         {
3995             isTarget: true,
3996             listeners : listeners || {} 
3997            
3998         
3999         }
4000     );
4001
4002 };
4003
4004 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
4005     /**
4006      * @cfg {String} overClass
4007      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
4008      */
4009      /**
4010      * @cfg {String} ddGroup
4011      * The drag drop group to handle drop events for
4012      */
4013      
4014     /**
4015      * @cfg {String} dropAllowed
4016      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
4017      */
4018     dropAllowed : "x-dd-drop-ok",
4019     /**
4020      * @cfg {String} dropNotAllowed
4021      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
4022      */
4023     dropNotAllowed : "x-dd-drop-nodrop",
4024     /**
4025      * @cfg {boolean} success
4026      * set this after drop listener.. 
4027      */
4028     success : false,
4029     /**
4030      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4031      * if the drop point is valid for over/enter..
4032      */
4033     valid : false,
4034     // private
4035     isTarget : true,
4036
4037     // private
4038     isNotifyTarget : true,
4039     
4040     /**
4041      * @hide
4042      */
4043     notifyEnter : function(dd, e, data)
4044     {
4045         this.valid = true;
4046         this.fireEvent('enter', dd, e, data);
4047         if(this.overClass){
4048             this.el.addClass(this.overClass);
4049         }
4050         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4051             this.valid ? this.dropAllowed : this.dropNotAllowed
4052         );
4053     },
4054
4055     /**
4056      * @hide
4057      */
4058     notifyOver : function(dd, e, data)
4059     {
4060         this.valid = true;
4061         this.fireEvent('over', dd, e, data);
4062         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4063             this.valid ? this.dropAllowed : this.dropNotAllowed
4064         );
4065     },
4066
4067     /**
4068      * @hide
4069      */
4070     notifyOut : function(dd, e, data)
4071     {
4072         this.fireEvent('out', dd, e, data);
4073         if(this.overClass){
4074             this.el.removeClass(this.overClass);
4075         }
4076     },
4077
4078     /**
4079      * @hide
4080      */
4081     notifyDrop : function(dd, e, data)
4082     {
4083         this.success = false;
4084         this.fireEvent('drop', dd, e, data);
4085         return this.success;
4086     }
4087 });/*
4088  * Based on:
4089  * Ext JS Library 1.1.1
4090  * Copyright(c) 2006-2007, Ext JS, LLC.
4091  *
4092  * Originally Released Under LGPL - original licence link has changed is not relivant.
4093  *
4094  * Fork - LGPL
4095  * <script type="text/javascript">
4096  */
4097
4098
4099 /**
4100  * @class Roo.dd.DragZone
4101  * @extends Roo.dd.DragSource
4102  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4103  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4104  * @constructor
4105  * @param {String/HTMLElement/Element} el The container element
4106  * @param {Object} config
4107  */
4108 Roo.dd.DragZone = function(el, config){
4109     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4110     if(this.containerScroll){
4111         Roo.dd.ScrollManager.register(this.el);
4112     }
4113 };
4114
4115 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4116     /**
4117      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4118      * for auto scrolling during drag operations.
4119      */
4120     /**
4121      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4122      * method after a failed drop (defaults to "c3daf9" - light blue)
4123      */
4124
4125     /**
4126      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4127      * for a valid target to drag based on the mouse down. Override this method
4128      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4129      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4130      * @param {EventObject} e The mouse down event
4131      * @return {Object} The dragData
4132      */
4133     getDragData : function(e){
4134         return Roo.dd.Registry.getHandleFromEvent(e);
4135     },
4136     
4137     /**
4138      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4139      * this.dragData.ddel
4140      * @param {Number} x The x position of the click on the dragged object
4141      * @param {Number} y The y position of the click on the dragged object
4142      * @return {Boolean} true to continue the drag, false to cancel
4143      */
4144     onInitDrag : function(x, y){
4145         this.proxy.update(this.dragData.ddel.cloneNode(true));
4146         this.onStartDrag(x, y);
4147         return true;
4148     },
4149     
4150     /**
4151      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4152      */
4153     afterRepair : function(){
4154         if(Roo.enableFx){
4155             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4156         }
4157         this.dragging = false;
4158     },
4159
4160     /**
4161      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4162      * the XY of this.dragData.ddel
4163      * @param {EventObject} e The mouse up event
4164      * @return {Array} The xy location (e.g. [100, 200])
4165      */
4166     getRepairXY : function(e){
4167         return Roo.Element.fly(this.dragData.ddel).getXY();  
4168     }
4169 });/*
4170  * Based on:
4171  * Ext JS Library 1.1.1
4172  * Copyright(c) 2006-2007, Ext JS, LLC.
4173  *
4174  * Originally Released Under LGPL - original licence link has changed is not relivant.
4175  *
4176  * Fork - LGPL
4177  * <script type="text/javascript">
4178  */
4179 /**
4180  * @class Roo.dd.DropZone
4181  * @extends Roo.dd.DropTarget
4182  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4183  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4184  * @constructor
4185  * @param {String/HTMLElement/Element} el The container element
4186  * @param {Object} config
4187  */
4188 Roo.dd.DropZone = function(el, config){
4189     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4190 };
4191
4192 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4193     /**
4194      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4195      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4196      * provide your own custom lookup.
4197      * @param {Event} e The event
4198      * @return {Object} data The custom data
4199      */
4200     getTargetFromEvent : function(e){
4201         return Roo.dd.Registry.getTargetFromEvent(e);
4202     },
4203
4204     /**
4205      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4206      * that it has registered.  This method has no default implementation and should be overridden to provide
4207      * node-specific processing if necessary.
4208      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4209      * {@link #getTargetFromEvent} for this node)
4210      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4211      * @param {Event} e The event
4212      * @param {Object} data An object containing arbitrary data supplied by the drag source
4213      */
4214     onNodeEnter : function(n, dd, e, data){
4215         
4216     },
4217
4218     /**
4219      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4220      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4221      * overridden to provide the proper feedback.
4222      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4223      * {@link #getTargetFromEvent} for this node)
4224      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4225      * @param {Event} e The event
4226      * @param {Object} data An object containing arbitrary data supplied by the drag source
4227      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4228      * underlying {@link Roo.dd.StatusProxy} can be updated
4229      */
4230     onNodeOver : function(n, dd, e, data){
4231         return this.dropAllowed;
4232     },
4233
4234     /**
4235      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4236      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4237      * node-specific processing if necessary.
4238      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4239      * {@link #getTargetFromEvent} for this node)
4240      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4241      * @param {Event} e The event
4242      * @param {Object} data An object containing arbitrary data supplied by the drag source
4243      */
4244     onNodeOut : function(n, dd, e, data){
4245         
4246     },
4247
4248     /**
4249      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4250      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4251      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4252      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4253      * {@link #getTargetFromEvent} for this node)
4254      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4255      * @param {Event} e The event
4256      * @param {Object} data An object containing arbitrary data supplied by the drag source
4257      * @return {Boolean} True if the drop was valid, else false
4258      */
4259     onNodeDrop : function(n, dd, e, data){
4260         return false;
4261     },
4262
4263     /**
4264      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4265      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4266      * it should be overridden to provide the proper feedback if necessary.
4267      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4268      * @param {Event} e The event
4269      * @param {Object} data An object containing arbitrary data supplied by the drag source
4270      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4271      * underlying {@link Roo.dd.StatusProxy} can be updated
4272      */
4273     onContainerOver : function(dd, e, data){
4274         return this.dropNotAllowed;
4275     },
4276
4277     /**
4278      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4279      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4280      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4281      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4282      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4283      * @param {Event} e The event
4284      * @param {Object} data An object containing arbitrary data supplied by the drag source
4285      * @return {Boolean} True if the drop was valid, else false
4286      */
4287     onContainerDrop : function(dd, e, data){
4288         return false;
4289     },
4290
4291     /**
4292      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4293      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4294      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4295      * you should override this method and provide a custom implementation.
4296      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4297      * @param {Event} e The event
4298      * @param {Object} data An object containing arbitrary data supplied by the drag source
4299      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4300      * underlying {@link Roo.dd.StatusProxy} can be updated
4301      */
4302     notifyEnter : function(dd, e, data){
4303         return this.dropNotAllowed;
4304     },
4305
4306     /**
4307      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4308      * This method will be called on every mouse movement while the drag source is over the drop zone.
4309      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4310      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4311      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4312      * registered node, it will call {@link #onContainerOver}.
4313      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4314      * @param {Event} e The event
4315      * @param {Object} data An object containing arbitrary data supplied by the drag source
4316      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4317      * underlying {@link Roo.dd.StatusProxy} can be updated
4318      */
4319     notifyOver : function(dd, e, data){
4320         var n = this.getTargetFromEvent(e);
4321         if(!n){ // not over valid drop target
4322             if(this.lastOverNode){
4323                 this.onNodeOut(this.lastOverNode, dd, e, data);
4324                 this.lastOverNode = null;
4325             }
4326             return this.onContainerOver(dd, e, data);
4327         }
4328         if(this.lastOverNode != n){
4329             if(this.lastOverNode){
4330                 this.onNodeOut(this.lastOverNode, dd, e, data);
4331             }
4332             this.onNodeEnter(n, dd, e, data);
4333             this.lastOverNode = n;
4334         }
4335         return this.onNodeOver(n, dd, e, data);
4336     },
4337
4338     /**
4339      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4340      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4341      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4342      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4343      * @param {Event} e The event
4344      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4345      */
4346     notifyOut : function(dd, e, data){
4347         if(this.lastOverNode){
4348             this.onNodeOut(this.lastOverNode, dd, e, data);
4349             this.lastOverNode = null;
4350         }
4351     },
4352
4353     /**
4354      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4355      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4356      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4357      * otherwise it will call {@link #onContainerDrop}.
4358      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4359      * @param {Event} e The event
4360      * @param {Object} data An object containing arbitrary data supplied by the drag source
4361      * @return {Boolean} True if the drop was valid, else false
4362      */
4363     notifyDrop : function(dd, e, data){
4364         if(this.lastOverNode){
4365             this.onNodeOut(this.lastOverNode, dd, e, data);
4366             this.lastOverNode = null;
4367         }
4368         var n = this.getTargetFromEvent(e);
4369         return n ?
4370             this.onNodeDrop(n, dd, e, data) :
4371             this.onContainerDrop(dd, e, data);
4372     },
4373
4374     // private
4375     triggerCacheRefresh : function(){
4376         Roo.dd.DDM.refreshCache(this.groups);
4377     }  
4378 });/*
4379  * Based on:
4380  * Ext JS Library 1.1.1
4381  * Copyright(c) 2006-2007, Ext JS, LLC.
4382  *
4383  * Originally Released Under LGPL - original licence link has changed is not relivant.
4384  *
4385  * Fork - LGPL
4386  * <script type="text/javascript">
4387  */
4388
4389
4390 /**
4391  * @class Roo.data.SortTypes
4392  * @singleton
4393  * Defines the default sorting (casting?) comparison functions used when sorting data.
4394  */
4395 Roo.data.SortTypes = {
4396     /**
4397      * Default sort that does nothing
4398      * @param {Mixed} s The value being converted
4399      * @return {Mixed} The comparison value
4400      */
4401     none : function(s){
4402         return s;
4403     },
4404     
4405     /**
4406      * The regular expression used to strip tags
4407      * @type {RegExp}
4408      * @property
4409      */
4410     stripTagsRE : /<\/?[^>]+>/gi,
4411     
4412     /**
4413      * Strips all HTML tags to sort on text only
4414      * @param {Mixed} s The value being converted
4415      * @return {String} The comparison value
4416      */
4417     asText : function(s){
4418         return String(s).replace(this.stripTagsRE, "");
4419     },
4420     
4421     /**
4422      * Strips all HTML tags to sort on text only - Case insensitive
4423      * @param {Mixed} s The value being converted
4424      * @return {String} The comparison value
4425      */
4426     asUCText : function(s){
4427         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4428     },
4429     
4430     /**
4431      * Case insensitive string
4432      * @param {Mixed} s The value being converted
4433      * @return {String} The comparison value
4434      */
4435     asUCString : function(s) {
4436         return String(s).toUpperCase();
4437     },
4438     
4439     /**
4440      * Date sorting
4441      * @param {Mixed} s The value being converted
4442      * @return {Number} The comparison value
4443      */
4444     asDate : function(s) {
4445         if(!s){
4446             return 0;
4447         }
4448         if(s instanceof Date){
4449             return s.getTime();
4450         }
4451         return Date.parse(String(s));
4452     },
4453     
4454     /**
4455      * Float sorting
4456      * @param {Mixed} s The value being converted
4457      * @return {Float} The comparison value
4458      */
4459     asFloat : function(s) {
4460         var val = parseFloat(String(s).replace(/,/g, ""));
4461         if(isNaN(val)) {
4462             val = 0;
4463         }
4464         return val;
4465     },
4466     
4467     /**
4468      * Integer sorting
4469      * @param {Mixed} s The value being converted
4470      * @return {Number} The comparison value
4471      */
4472     asInt : function(s) {
4473         var val = parseInt(String(s).replace(/,/g, ""));
4474         if(isNaN(val)) {
4475             val = 0;
4476         }
4477         return val;
4478     }
4479 };/*
4480  * Based on:
4481  * Ext JS Library 1.1.1
4482  * Copyright(c) 2006-2007, Ext JS, LLC.
4483  *
4484  * Originally Released Under LGPL - original licence link has changed is not relivant.
4485  *
4486  * Fork - LGPL
4487  * <script type="text/javascript">
4488  */
4489
4490 /**
4491 * @class Roo.data.Record
4492  * Instances of this class encapsulate both record <em>definition</em> information, and record
4493  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4494  * to access Records cached in an {@link Roo.data.Store} object.<br>
4495  * <p>
4496  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4497  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4498  * objects.<br>
4499  * <p>
4500  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4501  * @constructor
4502  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4503  * {@link #create}. The parameters are the same.
4504  * @param {Array} data An associative Array of data values keyed by the field name.
4505  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4506  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4507  * not specified an integer id is generated.
4508  */
4509 Roo.data.Record = function(data, id){
4510     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4511     this.data = data;
4512 };
4513
4514 /**
4515  * Generate a constructor for a specific record layout.
4516  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4517  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4518  * Each field definition object may contain the following properties: <ul>
4519  * <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,
4520  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4521  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4522  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4523  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4524  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4525  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4526  * this may be omitted.</p></li>
4527  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4528  * <ul><li>auto (Default, implies no conversion)</li>
4529  * <li>string</li>
4530  * <li>int</li>
4531  * <li>float</li>
4532  * <li>boolean</li>
4533  * <li>date</li></ul></p></li>
4534  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4535  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4536  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4537  * by the Reader into an object that will be stored in the Record. It is passed the
4538  * following parameters:<ul>
4539  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4540  * </ul></p></li>
4541  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4542  * </ul>
4543  * <br>usage:<br><pre><code>
4544 var TopicRecord = Roo.data.Record.create(
4545     {name: 'title', mapping: 'topic_title'},
4546     {name: 'author', mapping: 'username'},
4547     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4548     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4549     {name: 'lastPoster', mapping: 'user2'},
4550     {name: 'excerpt', mapping: 'post_text'}
4551 );
4552
4553 var myNewRecord = new TopicRecord({
4554     title: 'Do my job please',
4555     author: 'noobie',
4556     totalPosts: 1,
4557     lastPost: new Date(),
4558     lastPoster: 'Animal',
4559     excerpt: 'No way dude!'
4560 });
4561 myStore.add(myNewRecord);
4562 </code></pre>
4563  * @method create
4564  * @static
4565  */
4566 Roo.data.Record.create = function(o){
4567     var f = function(){
4568         f.superclass.constructor.apply(this, arguments);
4569     };
4570     Roo.extend(f, Roo.data.Record);
4571     var p = f.prototype;
4572     p.fields = new Roo.util.MixedCollection(false, function(field){
4573         return field.name;
4574     });
4575     for(var i = 0, len = o.length; i < len; i++){
4576         p.fields.add(new Roo.data.Field(o[i]));
4577     }
4578     f.getField = function(name){
4579         return p.fields.get(name);  
4580     };
4581     return f;
4582 };
4583
4584 Roo.data.Record.AUTO_ID = 1000;
4585 Roo.data.Record.EDIT = 'edit';
4586 Roo.data.Record.REJECT = 'reject';
4587 Roo.data.Record.COMMIT = 'commit';
4588
4589 Roo.data.Record.prototype = {
4590     /**
4591      * Readonly flag - true if this record has been modified.
4592      * @type Boolean
4593      */
4594     dirty : false,
4595     editing : false,
4596     error: null,
4597     modified: null,
4598
4599     // private
4600     join : function(store){
4601         this.store = store;
4602     },
4603
4604     /**
4605      * Set the named field to the specified value.
4606      * @param {String} name The name of the field to set.
4607      * @param {Object} value The value to set the field to.
4608      */
4609     set : function(name, value){
4610         if(this.data[name] == value){
4611             return;
4612         }
4613         this.dirty = true;
4614         if(!this.modified){
4615             this.modified = {};
4616         }
4617         if(typeof this.modified[name] == 'undefined'){
4618             this.modified[name] = this.data[name];
4619         }
4620         this.data[name] = value;
4621         if(!this.editing && this.store){
4622             this.store.afterEdit(this);
4623         }       
4624     },
4625
4626     /**
4627      * Get the value of the named field.
4628      * @param {String} name The name of the field to get the value of.
4629      * @return {Object} The value of the field.
4630      */
4631     get : function(name){
4632         return this.data[name]; 
4633     },
4634
4635     // private
4636     beginEdit : function(){
4637         this.editing = true;
4638         this.modified = {}; 
4639     },
4640
4641     // private
4642     cancelEdit : function(){
4643         this.editing = false;
4644         delete this.modified;
4645     },
4646
4647     // private
4648     endEdit : function(){
4649         this.editing = false;
4650         if(this.dirty && this.store){
4651             this.store.afterEdit(this);
4652         }
4653     },
4654
4655     /**
4656      * Usually called by the {@link Roo.data.Store} which owns the Record.
4657      * Rejects all changes made to the Record since either creation, or the last commit operation.
4658      * Modified fields are reverted to their original values.
4659      * <p>
4660      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4661      * of reject operations.
4662      */
4663     reject : function(){
4664         var m = this.modified;
4665         for(var n in m){
4666             if(typeof m[n] != "function"){
4667                 this.data[n] = m[n];
4668             }
4669         }
4670         this.dirty = false;
4671         delete this.modified;
4672         this.editing = false;
4673         if(this.store){
4674             this.store.afterReject(this);
4675         }
4676     },
4677
4678     /**
4679      * Usually called by the {@link Roo.data.Store} which owns the Record.
4680      * Commits all changes made to the Record since either creation, or the last commit operation.
4681      * <p>
4682      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4683      * of commit operations.
4684      */
4685     commit : function(){
4686         this.dirty = false;
4687         delete this.modified;
4688         this.editing = false;
4689         if(this.store){
4690             this.store.afterCommit(this);
4691         }
4692     },
4693
4694     // private
4695     hasError : function(){
4696         return this.error != null;
4697     },
4698
4699     // private
4700     clearError : function(){
4701         this.error = null;
4702     },
4703
4704     /**
4705      * Creates a copy of this record.
4706      * @param {String} id (optional) A new record id if you don't want to use this record's id
4707      * @return {Record}
4708      */
4709     copy : function(newId) {
4710         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4711     }
4712 };/*
4713  * Based on:
4714  * Ext JS Library 1.1.1
4715  * Copyright(c) 2006-2007, Ext JS, LLC.
4716  *
4717  * Originally Released Under LGPL - original licence link has changed is not relivant.
4718  *
4719  * Fork - LGPL
4720  * <script type="text/javascript">
4721  */
4722
4723
4724
4725 /**
4726  * @class Roo.data.Store
4727  * @extends Roo.util.Observable
4728  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4729  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4730  * <p>
4731  * 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
4732  * has no knowledge of the format of the data returned by the Proxy.<br>
4733  * <p>
4734  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4735  * instances from the data object. These records are cached and made available through accessor functions.
4736  * @constructor
4737  * Creates a new Store.
4738  * @param {Object} config A config object containing the objects needed for the Store to access data,
4739  * and read the data into Records.
4740  */
4741 Roo.data.Store = function(config){
4742     this.data = new Roo.util.MixedCollection(false);
4743     this.data.getKey = function(o){
4744         return o.id;
4745     };
4746     this.baseParams = {};
4747     // private
4748     this.paramNames = {
4749         "start" : "start",
4750         "limit" : "limit",
4751         "sort" : "sort",
4752         "dir" : "dir",
4753         "multisort" : "_multisort"
4754     };
4755
4756     if(config && config.data){
4757         this.inlineData = config.data;
4758         delete config.data;
4759     }
4760
4761     Roo.apply(this, config);
4762     
4763     if(this.reader){ // reader passed
4764         this.reader = Roo.factory(this.reader, Roo.data);
4765         this.reader.xmodule = this.xmodule || false;
4766         if(!this.recordType){
4767             this.recordType = this.reader.recordType;
4768         }
4769         if(this.reader.onMetaChange){
4770             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4771         }
4772     }
4773
4774     if(this.recordType){
4775         this.fields = this.recordType.prototype.fields;
4776     }
4777     this.modified = [];
4778
4779     this.addEvents({
4780         /**
4781          * @event datachanged
4782          * Fires when the data cache has changed, and a widget which is using this Store
4783          * as a Record cache should refresh its view.
4784          * @param {Store} this
4785          */
4786         datachanged : true,
4787         /**
4788          * @event metachange
4789          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4790          * @param {Store} this
4791          * @param {Object} meta The JSON metadata
4792          */
4793         metachange : true,
4794         /**
4795          * @event add
4796          * Fires when Records have been added to the Store
4797          * @param {Store} this
4798          * @param {Roo.data.Record[]} records The array of Records added
4799          * @param {Number} index The index at which the record(s) were added
4800          */
4801         add : true,
4802         /**
4803          * @event remove
4804          * Fires when a Record has been removed from the Store
4805          * @param {Store} this
4806          * @param {Roo.data.Record} record The Record that was removed
4807          * @param {Number} index The index at which the record was removed
4808          */
4809         remove : true,
4810         /**
4811          * @event update
4812          * Fires when a Record has been updated
4813          * @param {Store} this
4814          * @param {Roo.data.Record} record The Record that was updated
4815          * @param {String} operation The update operation being performed.  Value may be one of:
4816          * <pre><code>
4817  Roo.data.Record.EDIT
4818  Roo.data.Record.REJECT
4819  Roo.data.Record.COMMIT
4820          * </code></pre>
4821          */
4822         update : true,
4823         /**
4824          * @event clear
4825          * Fires when the data cache has been cleared.
4826          * @param {Store} this
4827          */
4828         clear : true,
4829         /**
4830          * @event beforeload
4831          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4832          * the load action will be canceled.
4833          * @param {Store} this
4834          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4835          */
4836         beforeload : true,
4837         /**
4838          * @event beforeloadadd
4839          * Fires after a new set of Records has been loaded.
4840          * @param {Store} this
4841          * @param {Roo.data.Record[]} records The Records that were loaded
4842          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4843          */
4844         beforeloadadd : true,
4845         /**
4846          * @event load
4847          * Fires after a new set of Records has been loaded, before they are added to the store.
4848          * @param {Store} this
4849          * @param {Roo.data.Record[]} records The Records that were loaded
4850          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4851          * @params {Object} return from reader
4852          */
4853         load : true,
4854         /**
4855          * @event loadexception
4856          * Fires if an exception occurs in the Proxy during loading.
4857          * Called with the signature of the Proxy's "loadexception" event.
4858          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4859          * 
4860          * @param {Proxy} 
4861          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4862          * @param {Object} load options 
4863          * @param {Object} jsonData from your request (normally this contains the Exception)
4864          */
4865         loadexception : true
4866     });
4867     
4868     if(this.proxy){
4869         this.proxy = Roo.factory(this.proxy, Roo.data);
4870         this.proxy.xmodule = this.xmodule || false;
4871         this.relayEvents(this.proxy,  ["loadexception"]);
4872     }
4873     this.sortToggle = {};
4874     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4875
4876     Roo.data.Store.superclass.constructor.call(this);
4877
4878     if(this.inlineData){
4879         this.loadData(this.inlineData);
4880         delete this.inlineData;
4881     }
4882 };
4883
4884 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4885      /**
4886     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4887     * without a remote query - used by combo/forms at present.
4888     */
4889     
4890     /**
4891     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4892     */
4893     /**
4894     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4895     */
4896     /**
4897     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4898     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4899     */
4900     /**
4901     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4902     * on any HTTP request
4903     */
4904     /**
4905     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4906     */
4907     /**
4908     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4909     */
4910     multiSort: false,
4911     /**
4912     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4913     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4914     */
4915     remoteSort : false,
4916
4917     /**
4918     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4919      * loaded or when a record is removed. (defaults to false).
4920     */
4921     pruneModifiedRecords : false,
4922
4923     // private
4924     lastOptions : null,
4925
4926     /**
4927      * Add Records to the Store and fires the add event.
4928      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4929      */
4930     add : function(records){
4931         records = [].concat(records);
4932         for(var i = 0, len = records.length; i < len; i++){
4933             records[i].join(this);
4934         }
4935         var index = this.data.length;
4936         this.data.addAll(records);
4937         this.fireEvent("add", this, records, index);
4938     },
4939
4940     /**
4941      * Remove a Record from the Store and fires the remove event.
4942      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4943      */
4944     remove : function(record){
4945         var index = this.data.indexOf(record);
4946         this.data.removeAt(index);
4947         if(this.pruneModifiedRecords){
4948             this.modified.remove(record);
4949         }
4950         this.fireEvent("remove", this, record, index);
4951     },
4952
4953     /**
4954      * Remove all Records from the Store and fires the clear event.
4955      */
4956     removeAll : function(){
4957         this.data.clear();
4958         if(this.pruneModifiedRecords){
4959             this.modified = [];
4960         }
4961         this.fireEvent("clear", this);
4962     },
4963
4964     /**
4965      * Inserts Records to the Store at the given index and fires the add event.
4966      * @param {Number} index The start index at which to insert the passed Records.
4967      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4968      */
4969     insert : function(index, records){
4970         records = [].concat(records);
4971         for(var i = 0, len = records.length; i < len; i++){
4972             this.data.insert(index, records[i]);
4973             records[i].join(this);
4974         }
4975         this.fireEvent("add", this, records, index);
4976     },
4977
4978     /**
4979      * Get the index within the cache of the passed Record.
4980      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4981      * @return {Number} The index of the passed Record. Returns -1 if not found.
4982      */
4983     indexOf : function(record){
4984         return this.data.indexOf(record);
4985     },
4986
4987     /**
4988      * Get the index within the cache of the Record with the passed id.
4989      * @param {String} id The id of the Record to find.
4990      * @return {Number} The index of the Record. Returns -1 if not found.
4991      */
4992     indexOfId : function(id){
4993         return this.data.indexOfKey(id);
4994     },
4995
4996     /**
4997      * Get the Record with the specified id.
4998      * @param {String} id The id of the Record to find.
4999      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
5000      */
5001     getById : function(id){
5002         return this.data.key(id);
5003     },
5004
5005     /**
5006      * Get the Record at the specified index.
5007      * @param {Number} index The index of the Record to find.
5008      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
5009      */
5010     getAt : function(index){
5011         return this.data.itemAt(index);
5012     },
5013
5014     /**
5015      * Returns a range of Records between specified indices.
5016      * @param {Number} startIndex (optional) The starting index (defaults to 0)
5017      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
5018      * @return {Roo.data.Record[]} An array of Records
5019      */
5020     getRange : function(start, end){
5021         return this.data.getRange(start, end);
5022     },
5023
5024     // private
5025     storeOptions : function(o){
5026         o = Roo.apply({}, o);
5027         delete o.callback;
5028         delete o.scope;
5029         this.lastOptions = o;
5030     },
5031
5032     /**
5033      * Loads the Record cache from the configured Proxy using the configured Reader.
5034      * <p>
5035      * If using remote paging, then the first load call must specify the <em>start</em>
5036      * and <em>limit</em> properties in the options.params property to establish the initial
5037      * position within the dataset, and the number of Records to cache on each read from the Proxy.
5038      * <p>
5039      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5040      * and this call will return before the new data has been loaded. Perform any post-processing
5041      * in a callback function, or in a "load" event handler.</strong>
5042      * <p>
5043      * @param {Object} options An object containing properties which control loading options:<ul>
5044      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5045      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5046      * passed the following arguments:<ul>
5047      * <li>r : Roo.data.Record[]</li>
5048      * <li>options: Options object from the load call</li>
5049      * <li>success: Boolean success indicator</li></ul></li>
5050      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5051      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5052      * </ul>
5053      */
5054     load : function(options){
5055         options = options || {};
5056         if(this.fireEvent("beforeload", this, options) !== false){
5057             this.storeOptions(options);
5058             var p = Roo.apply(options.params || {}, this.baseParams);
5059             // if meta was not loaded from remote source.. try requesting it.
5060             if (!this.reader.metaFromRemote) {
5061                 p._requestMeta = 1;
5062             }
5063             if(this.sortInfo && this.remoteSort){
5064                 var pn = this.paramNames;
5065                 p[pn["sort"]] = this.sortInfo.field;
5066                 p[pn["dir"]] = this.sortInfo.direction;
5067             }
5068             if (this.multiSort) {
5069                 var pn = this.paramNames;
5070                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5071             }
5072             
5073             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5074         }
5075     },
5076
5077     /**
5078      * Reloads the Record cache from the configured Proxy using the configured Reader and
5079      * the options from the last load operation performed.
5080      * @param {Object} options (optional) An object containing properties which may override the options
5081      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5082      * the most recently used options are reused).
5083      */
5084     reload : function(options){
5085         this.load(Roo.applyIf(options||{}, this.lastOptions));
5086     },
5087
5088     // private
5089     // Called as a callback by the Reader during a load operation.
5090     loadRecords : function(o, options, success){
5091         if(!o || success === false){
5092             if(success !== false){
5093                 this.fireEvent("load", this, [], options, o);
5094             }
5095             if(options.callback){
5096                 options.callback.call(options.scope || this, [], options, false);
5097             }
5098             return;
5099         }
5100         // if data returned failure - throw an exception.
5101         if (o.success === false) {
5102             // show a message if no listener is registered.
5103             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
5104                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
5105             }
5106             // loadmask wil be hooked into this..
5107             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
5108             return;
5109         }
5110         var r = o.records, t = o.totalRecords || r.length;
5111         
5112         this.fireEvent("beforeloadadd", this, r, options, o);
5113         
5114         if(!options || options.add !== true){
5115             if(this.pruneModifiedRecords){
5116                 this.modified = [];
5117             }
5118             for(var i = 0, len = r.length; i < len; i++){
5119                 r[i].join(this);
5120             }
5121             if(this.snapshot){
5122                 this.data = this.snapshot;
5123                 delete this.snapshot;
5124             }
5125             this.data.clear();
5126             this.data.addAll(r);
5127             this.totalLength = t;
5128             this.applySort();
5129             this.fireEvent("datachanged", this);
5130         }else{
5131             this.totalLength = Math.max(t, this.data.length+r.length);
5132             this.add(r);
5133         }
5134         this.fireEvent("load", this, r, options, o);
5135         if(options.callback){
5136             options.callback.call(options.scope || this, r, options, true);
5137         }
5138     },
5139
5140
5141     /**
5142      * Loads data from a passed data block. A Reader which understands the format of the data
5143      * must have been configured in the constructor.
5144      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5145      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5146      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5147      */
5148     loadData : function(o, append){
5149         var r = this.reader.readRecords(o);
5150         this.loadRecords(r, {add: append}, true);
5151     },
5152
5153     /**
5154      * Gets the number of cached records.
5155      * <p>
5156      * <em>If using paging, this may not be the total size of the dataset. If the data object
5157      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5158      * the data set size</em>
5159      */
5160     getCount : function(){
5161         return this.data.length || 0;
5162     },
5163
5164     /**
5165      * Gets the total number of records in the dataset as returned by the server.
5166      * <p>
5167      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5168      * the dataset size</em>
5169      */
5170     getTotalCount : function(){
5171         return this.totalLength || 0;
5172     },
5173
5174     /**
5175      * Returns the sort state of the Store as an object with two properties:
5176      * <pre><code>
5177  field {String} The name of the field by which the Records are sorted
5178  direction {String} The sort order, "ASC" or "DESC"
5179      * </code></pre>
5180      */
5181     getSortState : function(){
5182         return this.sortInfo;
5183     },
5184
5185     // private
5186     applySort : function(){
5187         if(this.sortInfo && !this.remoteSort){
5188             var s = this.sortInfo, f = s.field;
5189             var st = this.fields.get(f).sortType;
5190             var fn = function(r1, r2){
5191                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5192                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5193             };
5194             this.data.sort(s.direction, fn);
5195             if(this.snapshot && this.snapshot != this.data){
5196                 this.snapshot.sort(s.direction, fn);
5197             }
5198         }
5199     },
5200
5201     /**
5202      * Sets the default sort column and order to be used by the next load operation.
5203      * @param {String} fieldName The name of the field to sort by.
5204      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5205      */
5206     setDefaultSort : function(field, dir){
5207         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5208     },
5209
5210     /**
5211      * Sort the Records.
5212      * If remote sorting is used, the sort is performed on the server, and the cache is
5213      * reloaded. If local sorting is used, the cache is sorted internally.
5214      * @param {String} fieldName The name of the field to sort by.
5215      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5216      */
5217     sort : function(fieldName, dir){
5218         var f = this.fields.get(fieldName);
5219         if(!dir){
5220             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5221             
5222             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5223                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5224             }else{
5225                 dir = f.sortDir;
5226             }
5227         }
5228         this.sortToggle[f.name] = dir;
5229         this.sortInfo = {field: f.name, direction: dir};
5230         if(!this.remoteSort){
5231             this.applySort();
5232             this.fireEvent("datachanged", this);
5233         }else{
5234             this.load(this.lastOptions);
5235         }
5236     },
5237
5238     /**
5239      * Calls the specified function for each of the Records in the cache.
5240      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5241      * Returning <em>false</em> aborts and exits the iteration.
5242      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5243      */
5244     each : function(fn, scope){
5245         this.data.each(fn, scope);
5246     },
5247
5248     /**
5249      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5250      * (e.g., during paging).
5251      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5252      */
5253     getModifiedRecords : function(){
5254         return this.modified;
5255     },
5256
5257     // private
5258     createFilterFn : function(property, value, anyMatch){
5259         if(!value.exec){ // not a regex
5260             value = String(value);
5261             if(value.length == 0){
5262                 return false;
5263             }
5264             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5265         }
5266         return function(r){
5267             return value.test(r.data[property]);
5268         };
5269     },
5270
5271     /**
5272      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5273      * @param {String} property A field on your records
5274      * @param {Number} start The record index to start at (defaults to 0)
5275      * @param {Number} end The last record index to include (defaults to length - 1)
5276      * @return {Number} The sum
5277      */
5278     sum : function(property, start, end){
5279         var rs = this.data.items, v = 0;
5280         start = start || 0;
5281         end = (end || end === 0) ? end : rs.length-1;
5282
5283         for(var i = start; i <= end; i++){
5284             v += (rs[i].data[property] || 0);
5285         }
5286         return v;
5287     },
5288
5289     /**
5290      * Filter the records by a specified property.
5291      * @param {String} field A field on your records
5292      * @param {String/RegExp} value Either a string that the field
5293      * should start with or a RegExp to test against the field
5294      * @param {Boolean} anyMatch True to match any part not just the beginning
5295      */
5296     filter : function(property, value, anyMatch){
5297         var fn = this.createFilterFn(property, value, anyMatch);
5298         return fn ? this.filterBy(fn) : this.clearFilter();
5299     },
5300
5301     /**
5302      * Filter by a function. The specified function will be called with each
5303      * record in this data source. If the function returns true the record is included,
5304      * otherwise it is filtered.
5305      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5306      * @param {Object} scope (optional) The scope of the function (defaults to this)
5307      */
5308     filterBy : function(fn, scope){
5309         this.snapshot = this.snapshot || this.data;
5310         this.data = this.queryBy(fn, scope||this);
5311         this.fireEvent("datachanged", this);
5312     },
5313
5314     /**
5315      * Query the records by a specified property.
5316      * @param {String} field A field on your records
5317      * @param {String/RegExp} value Either a string that the field
5318      * should start with or a RegExp to test against the field
5319      * @param {Boolean} anyMatch True to match any part not just the beginning
5320      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5321      */
5322     query : function(property, value, anyMatch){
5323         var fn = this.createFilterFn(property, value, anyMatch);
5324         return fn ? this.queryBy(fn) : this.data.clone();
5325     },
5326
5327     /**
5328      * Query by a function. The specified function will be called with each
5329      * record in this data source. If the function returns true the record is included
5330      * in the results.
5331      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5332      * @param {Object} scope (optional) The scope of the function (defaults to this)
5333       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5334      **/
5335     queryBy : function(fn, scope){
5336         var data = this.snapshot || this.data;
5337         return data.filterBy(fn, scope||this);
5338     },
5339
5340     /**
5341      * Collects unique values for a particular dataIndex from this store.
5342      * @param {String} dataIndex The property to collect
5343      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5344      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5345      * @return {Array} An array of the unique values
5346      **/
5347     collect : function(dataIndex, allowNull, bypassFilter){
5348         var d = (bypassFilter === true && this.snapshot) ?
5349                 this.snapshot.items : this.data.items;
5350         var v, sv, r = [], l = {};
5351         for(var i = 0, len = d.length; i < len; i++){
5352             v = d[i].data[dataIndex];
5353             sv = String(v);
5354             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5355                 l[sv] = true;
5356                 r[r.length] = v;
5357             }
5358         }
5359         return r;
5360     },
5361
5362     /**
5363      * Revert to a view of the Record cache with no filtering applied.
5364      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5365      */
5366     clearFilter : function(suppressEvent){
5367         if(this.snapshot && this.snapshot != this.data){
5368             this.data = this.snapshot;
5369             delete this.snapshot;
5370             if(suppressEvent !== true){
5371                 this.fireEvent("datachanged", this);
5372             }
5373         }
5374     },
5375
5376     // private
5377     afterEdit : function(record){
5378         if(this.modified.indexOf(record) == -1){
5379             this.modified.push(record);
5380         }
5381         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5382     },
5383     
5384     // private
5385     afterReject : function(record){
5386         this.modified.remove(record);
5387         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5388     },
5389
5390     // private
5391     afterCommit : function(record){
5392         this.modified.remove(record);
5393         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5394     },
5395
5396     /**
5397      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5398      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5399      */
5400     commitChanges : function(){
5401         var m = this.modified.slice(0);
5402         this.modified = [];
5403         for(var i = 0, len = m.length; i < len; i++){
5404             m[i].commit();
5405         }
5406     },
5407
5408     /**
5409      * Cancel outstanding changes on all changed records.
5410      */
5411     rejectChanges : function(){
5412         var m = this.modified.slice(0);
5413         this.modified = [];
5414         for(var i = 0, len = m.length; i < len; i++){
5415             m[i].reject();
5416         }
5417     },
5418
5419     onMetaChange : function(meta, rtype, o){
5420         this.recordType = rtype;
5421         this.fields = rtype.prototype.fields;
5422         delete this.snapshot;
5423         this.sortInfo = meta.sortInfo || this.sortInfo;
5424         this.modified = [];
5425         this.fireEvent('metachange', this, this.reader.meta);
5426     },
5427     
5428     moveIndex : function(data, type)
5429     {
5430         var index = this.indexOf(data);
5431         
5432         var newIndex = index + type;
5433         
5434         this.remove(data);
5435         
5436         this.insert(newIndex, data);
5437         
5438     }
5439 });/*
5440  * Based on:
5441  * Ext JS Library 1.1.1
5442  * Copyright(c) 2006-2007, Ext JS, LLC.
5443  *
5444  * Originally Released Under LGPL - original licence link has changed is not relivant.
5445  *
5446  * Fork - LGPL
5447  * <script type="text/javascript">
5448  */
5449
5450 /**
5451  * @class Roo.data.SimpleStore
5452  * @extends Roo.data.Store
5453  * Small helper class to make creating Stores from Array data easier.
5454  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5455  * @cfg {Array} fields An array of field definition objects, or field name strings.
5456  * @cfg {Array} data The multi-dimensional array of data
5457  * @constructor
5458  * @param {Object} config
5459  */
5460 Roo.data.SimpleStore = function(config){
5461     Roo.data.SimpleStore.superclass.constructor.call(this, {
5462         isLocal : true,
5463         reader: new Roo.data.ArrayReader({
5464                 id: config.id
5465             },
5466             Roo.data.Record.create(config.fields)
5467         ),
5468         proxy : new Roo.data.MemoryProxy(config.data)
5469     });
5470     this.load();
5471 };
5472 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5473  * Based on:
5474  * Ext JS Library 1.1.1
5475  * Copyright(c) 2006-2007, Ext JS, LLC.
5476  *
5477  * Originally Released Under LGPL - original licence link has changed is not relivant.
5478  *
5479  * Fork - LGPL
5480  * <script type="text/javascript">
5481  */
5482
5483 /**
5484 /**
5485  * @extends Roo.data.Store
5486  * @class Roo.data.JsonStore
5487  * Small helper class to make creating Stores for JSON data easier. <br/>
5488 <pre><code>
5489 var store = new Roo.data.JsonStore({
5490     url: 'get-images.php',
5491     root: 'images',
5492     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5493 });
5494 </code></pre>
5495  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5496  * JsonReader and HttpProxy (unless inline data is provided).</b>
5497  * @cfg {Array} fields An array of field definition objects, or field name strings.
5498  * @constructor
5499  * @param {Object} config
5500  */
5501 Roo.data.JsonStore = function(c){
5502     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5503         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5504         reader: new Roo.data.JsonReader(c, c.fields)
5505     }));
5506 };
5507 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5508  * Based on:
5509  * Ext JS Library 1.1.1
5510  * Copyright(c) 2006-2007, Ext JS, LLC.
5511  *
5512  * Originally Released Under LGPL - original licence link has changed is not relivant.
5513  *
5514  * Fork - LGPL
5515  * <script type="text/javascript">
5516  */
5517
5518  
5519 Roo.data.Field = function(config){
5520     if(typeof config == "string"){
5521         config = {name: config};
5522     }
5523     Roo.apply(this, config);
5524     
5525     if(!this.type){
5526         this.type = "auto";
5527     }
5528     
5529     var st = Roo.data.SortTypes;
5530     // named sortTypes are supported, here we look them up
5531     if(typeof this.sortType == "string"){
5532         this.sortType = st[this.sortType];
5533     }
5534     
5535     // set default sortType for strings and dates
5536     if(!this.sortType){
5537         switch(this.type){
5538             case "string":
5539                 this.sortType = st.asUCString;
5540                 break;
5541             case "date":
5542                 this.sortType = st.asDate;
5543                 break;
5544             default:
5545                 this.sortType = st.none;
5546         }
5547     }
5548
5549     // define once
5550     var stripRe = /[\$,%]/g;
5551
5552     // prebuilt conversion function for this field, instead of
5553     // switching every time we're reading a value
5554     if(!this.convert){
5555         var cv, dateFormat = this.dateFormat;
5556         switch(this.type){
5557             case "":
5558             case "auto":
5559             case undefined:
5560                 cv = function(v){ return v; };
5561                 break;
5562             case "string":
5563                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5564                 break;
5565             case "int":
5566                 cv = function(v){
5567                     return v !== undefined && v !== null && v !== '' ?
5568                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5569                     };
5570                 break;
5571             case "float":
5572                 cv = function(v){
5573                     return v !== undefined && v !== null && v !== '' ?
5574                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5575                     };
5576                 break;
5577             case "bool":
5578             case "boolean":
5579                 cv = function(v){ return v === true || v === "true" || v == 1; };
5580                 break;
5581             case "date":
5582                 cv = function(v){
5583                     if(!v){
5584                         return '';
5585                     }
5586                     if(v instanceof Date){
5587                         return v;
5588                     }
5589                     if(dateFormat){
5590                         if(dateFormat == "timestamp"){
5591                             return new Date(v*1000);
5592                         }
5593                         return Date.parseDate(v, dateFormat);
5594                     }
5595                     var parsed = Date.parse(v);
5596                     return parsed ? new Date(parsed) : null;
5597                 };
5598              break;
5599             
5600         }
5601         this.convert = cv;
5602     }
5603 };
5604
5605 Roo.data.Field.prototype = {
5606     dateFormat: null,
5607     defaultValue: "",
5608     mapping: null,
5609     sortType : null,
5610     sortDir : "ASC"
5611 };/*
5612  * Based on:
5613  * Ext JS Library 1.1.1
5614  * Copyright(c) 2006-2007, Ext JS, LLC.
5615  *
5616  * Originally Released Under LGPL - original licence link has changed is not relivant.
5617  *
5618  * Fork - LGPL
5619  * <script type="text/javascript">
5620  */
5621  
5622 // Base class for reading structured data from a data source.  This class is intended to be
5623 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5624
5625 /**
5626  * @class Roo.data.DataReader
5627  * Base class for reading structured data from a data source.  This class is intended to be
5628  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5629  */
5630
5631 Roo.data.DataReader = function(meta, recordType){
5632     
5633     this.meta = meta;
5634     
5635     this.recordType = recordType instanceof Array ? 
5636         Roo.data.Record.create(recordType) : recordType;
5637 };
5638
5639 Roo.data.DataReader.prototype = {
5640      /**
5641      * Create an empty record
5642      * @param {Object} data (optional) - overlay some values
5643      * @return {Roo.data.Record} record created.
5644      */
5645     newRow :  function(d) {
5646         var da =  {};
5647         this.recordType.prototype.fields.each(function(c) {
5648             switch( c.type) {
5649                 case 'int' : da[c.name] = 0; break;
5650                 case 'date' : da[c.name] = new Date(); break;
5651                 case 'float' : da[c.name] = 0.0; break;
5652                 case 'boolean' : da[c.name] = false; break;
5653                 default : da[c.name] = ""; break;
5654             }
5655             
5656         });
5657         return new this.recordType(Roo.apply(da, d));
5658     }
5659     
5660 };/*
5661  * Based on:
5662  * Ext JS Library 1.1.1
5663  * Copyright(c) 2006-2007, Ext JS, LLC.
5664  *
5665  * Originally Released Under LGPL - original licence link has changed is not relivant.
5666  *
5667  * Fork - LGPL
5668  * <script type="text/javascript">
5669  */
5670
5671 /**
5672  * @class Roo.data.DataProxy
5673  * @extends Roo.data.Observable
5674  * This class is an abstract base class for implementations which provide retrieval of
5675  * unformatted data objects.<br>
5676  * <p>
5677  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5678  * (of the appropriate type which knows how to parse the data object) to provide a block of
5679  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5680  * <p>
5681  * Custom implementations must implement the load method as described in
5682  * {@link Roo.data.HttpProxy#load}.
5683  */
5684 Roo.data.DataProxy = function(){
5685     this.addEvents({
5686         /**
5687          * @event beforeload
5688          * Fires before a network request is made to retrieve a data object.
5689          * @param {Object} This DataProxy object.
5690          * @param {Object} params The params parameter to the load function.
5691          */
5692         beforeload : true,
5693         /**
5694          * @event load
5695          * Fires before the load method's callback is called.
5696          * @param {Object} This DataProxy object.
5697          * @param {Object} o The data object.
5698          * @param {Object} arg The callback argument object passed to the load function.
5699          */
5700         load : true,
5701         /**
5702          * @event loadexception
5703          * Fires if an Exception occurs during data retrieval.
5704          * @param {Object} This DataProxy object.
5705          * @param {Object} o The data object.
5706          * @param {Object} arg The callback argument object passed to the load function.
5707          * @param {Object} e The Exception.
5708          */
5709         loadexception : true
5710     });
5711     Roo.data.DataProxy.superclass.constructor.call(this);
5712 };
5713
5714 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5715
5716     /**
5717      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5718      */
5719 /*
5720  * Based on:
5721  * Ext JS Library 1.1.1
5722  * Copyright(c) 2006-2007, Ext JS, LLC.
5723  *
5724  * Originally Released Under LGPL - original licence link has changed is not relivant.
5725  *
5726  * Fork - LGPL
5727  * <script type="text/javascript">
5728  */
5729 /**
5730  * @class Roo.data.MemoryProxy
5731  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5732  * to the Reader when its load method is called.
5733  * @constructor
5734  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5735  */
5736 Roo.data.MemoryProxy = function(data){
5737     if (data.data) {
5738         data = data.data;
5739     }
5740     Roo.data.MemoryProxy.superclass.constructor.call(this);
5741     this.data = data;
5742 };
5743
5744 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5745     /**
5746      * Load data from the requested source (in this case an in-memory
5747      * data object passed to the constructor), read the data object into
5748      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5749      * process that block using the passed callback.
5750      * @param {Object} params This parameter is not used by the MemoryProxy class.
5751      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5752      * object into a block of Roo.data.Records.
5753      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5754      * The function must be passed <ul>
5755      * <li>The Record block object</li>
5756      * <li>The "arg" argument from the load function</li>
5757      * <li>A boolean success indicator</li>
5758      * </ul>
5759      * @param {Object} scope The scope in which to call the callback
5760      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5761      */
5762     load : function(params, reader, callback, scope, arg){
5763         params = params || {};
5764         var result;
5765         try {
5766             result = reader.readRecords(this.data);
5767         }catch(e){
5768             this.fireEvent("loadexception", this, arg, null, e);
5769             callback.call(scope, null, arg, false);
5770             return;
5771         }
5772         callback.call(scope, result, arg, true);
5773     },
5774     
5775     // private
5776     update : function(params, records){
5777         
5778     }
5779 });/*
5780  * Based on:
5781  * Ext JS Library 1.1.1
5782  * Copyright(c) 2006-2007, Ext JS, LLC.
5783  *
5784  * Originally Released Under LGPL - original licence link has changed is not relivant.
5785  *
5786  * Fork - LGPL
5787  * <script type="text/javascript">
5788  */
5789 /**
5790  * @class Roo.data.HttpProxy
5791  * @extends Roo.data.DataProxy
5792  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5793  * configured to reference a certain URL.<br><br>
5794  * <p>
5795  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5796  * from which the running page was served.<br><br>
5797  * <p>
5798  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5799  * <p>
5800  * Be aware that to enable the browser to parse an XML document, the server must set
5801  * the Content-Type header in the HTTP response to "text/xml".
5802  * @constructor
5803  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5804  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5805  * will be used to make the request.
5806  */
5807 Roo.data.HttpProxy = function(conn){
5808     Roo.data.HttpProxy.superclass.constructor.call(this);
5809     // is conn a conn config or a real conn?
5810     this.conn = conn;
5811     this.useAjax = !conn || !conn.events;
5812   
5813 };
5814
5815 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5816     // thse are take from connection...
5817     
5818     /**
5819      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5820      */
5821     /**
5822      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5823      * extra parameters to each request made by this object. (defaults to undefined)
5824      */
5825     /**
5826      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5827      *  to each request made by this object. (defaults to undefined)
5828      */
5829     /**
5830      * @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)
5831      */
5832     /**
5833      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5834      */
5835      /**
5836      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5837      * @type Boolean
5838      */
5839   
5840
5841     /**
5842      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5843      * @type Boolean
5844      */
5845     /**
5846      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5847      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5848      * a finer-grained basis than the DataProxy events.
5849      */
5850     getConnection : function(){
5851         return this.useAjax ? Roo.Ajax : this.conn;
5852     },
5853
5854     /**
5855      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5856      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5857      * process that block using the passed callback.
5858      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5859      * for the request to the remote server.
5860      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5861      * object into a block of Roo.data.Records.
5862      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5863      * The function must be passed <ul>
5864      * <li>The Record block object</li>
5865      * <li>The "arg" argument from the load function</li>
5866      * <li>A boolean success indicator</li>
5867      * </ul>
5868      * @param {Object} scope The scope in which to call the callback
5869      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5870      */
5871     load : function(params, reader, callback, scope, arg){
5872         if(this.fireEvent("beforeload", this, params) !== false){
5873             var  o = {
5874                 params : params || {},
5875                 request: {
5876                     callback : callback,
5877                     scope : scope,
5878                     arg : arg
5879                 },
5880                 reader: reader,
5881                 callback : this.loadResponse,
5882                 scope: this
5883             };
5884             if(this.useAjax){
5885                 Roo.applyIf(o, this.conn);
5886                 if(this.activeRequest){
5887                     Roo.Ajax.abort(this.activeRequest);
5888                 }
5889                 this.activeRequest = Roo.Ajax.request(o);
5890             }else{
5891                 this.conn.request(o);
5892             }
5893         }else{
5894             callback.call(scope||this, null, arg, false);
5895         }
5896     },
5897
5898     // private
5899     loadResponse : function(o, success, response){
5900         delete this.activeRequest;
5901         if(!success){
5902             this.fireEvent("loadexception", this, o, response);
5903             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5904             return;
5905         }
5906         var result;
5907         try {
5908             result = o.reader.read(response);
5909         }catch(e){
5910             this.fireEvent("loadexception", this, o, response, e);
5911             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5912             return;
5913         }
5914         
5915         this.fireEvent("load", this, o, o.request.arg);
5916         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5917     },
5918
5919     // private
5920     update : function(dataSet){
5921
5922     },
5923
5924     // private
5925     updateResponse : function(dataSet){
5926
5927     }
5928 });/*
5929  * Based on:
5930  * Ext JS Library 1.1.1
5931  * Copyright(c) 2006-2007, Ext JS, LLC.
5932  *
5933  * Originally Released Under LGPL - original licence link has changed is not relivant.
5934  *
5935  * Fork - LGPL
5936  * <script type="text/javascript">
5937  */
5938
5939 /**
5940  * @class Roo.data.ScriptTagProxy
5941  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5942  * other than the originating domain of the running page.<br><br>
5943  * <p>
5944  * <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
5945  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5946  * <p>
5947  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5948  * source code that is used as the source inside a &lt;script> tag.<br><br>
5949  * <p>
5950  * In order for the browser to process the returned data, the server must wrap the data object
5951  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5952  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5953  * depending on whether the callback name was passed:
5954  * <p>
5955  * <pre><code>
5956 boolean scriptTag = false;
5957 String cb = request.getParameter("callback");
5958 if (cb != null) {
5959     scriptTag = true;
5960     response.setContentType("text/javascript");
5961 } else {
5962     response.setContentType("application/x-json");
5963 }
5964 Writer out = response.getWriter();
5965 if (scriptTag) {
5966     out.write(cb + "(");
5967 }
5968 out.print(dataBlock.toJsonString());
5969 if (scriptTag) {
5970     out.write(");");
5971 }
5972 </pre></code>
5973  *
5974  * @constructor
5975  * @param {Object} config A configuration object.
5976  */
5977 Roo.data.ScriptTagProxy = function(config){
5978     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5979     Roo.apply(this, config);
5980     this.head = document.getElementsByTagName("head")[0];
5981 };
5982
5983 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5984
5985 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5986     /**
5987      * @cfg {String} url The URL from which to request the data object.
5988      */
5989     /**
5990      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5991      */
5992     timeout : 30000,
5993     /**
5994      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5995      * the server the name of the callback function set up by the load call to process the returned data object.
5996      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5997      * javascript output which calls this named function passing the data object as its only parameter.
5998      */
5999     callbackParam : "callback",
6000     /**
6001      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
6002      * name to the request.
6003      */
6004     nocache : true,
6005
6006     /**
6007      * Load data from the configured URL, read the data object into
6008      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
6009      * process that block using the passed callback.
6010      * @param {Object} params An object containing properties which are to be used as HTTP parameters
6011      * for the request to the remote server.
6012      * @param {Roo.data.DataReader} reader The Reader object which converts the data
6013      * object into a block of Roo.data.Records.
6014      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
6015      * The function must be passed <ul>
6016      * <li>The Record block object</li>
6017      * <li>The "arg" argument from the load function</li>
6018      * <li>A boolean success indicator</li>
6019      * </ul>
6020      * @param {Object} scope The scope in which to call the callback
6021      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
6022      */
6023     load : function(params, reader, callback, scope, arg){
6024         if(this.fireEvent("beforeload", this, params) !== false){
6025
6026             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
6027
6028             var url = this.url;
6029             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
6030             if(this.nocache){
6031                 url += "&_dc=" + (new Date().getTime());
6032             }
6033             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
6034             var trans = {
6035                 id : transId,
6036                 cb : "stcCallback"+transId,
6037                 scriptId : "stcScript"+transId,
6038                 params : params,
6039                 arg : arg,
6040                 url : url,
6041                 callback : callback,
6042                 scope : scope,
6043                 reader : reader
6044             };
6045             var conn = this;
6046
6047             window[trans.cb] = function(o){
6048                 conn.handleResponse(o, trans);
6049             };
6050
6051             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
6052
6053             if(this.autoAbort !== false){
6054                 this.abort();
6055             }
6056
6057             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6058
6059             var script = document.createElement("script");
6060             script.setAttribute("src", url);
6061             script.setAttribute("type", "text/javascript");
6062             script.setAttribute("id", trans.scriptId);
6063             this.head.appendChild(script);
6064
6065             this.trans = trans;
6066         }else{
6067             callback.call(scope||this, null, arg, false);
6068         }
6069     },
6070
6071     // private
6072     isLoading : function(){
6073         return this.trans ? true : false;
6074     },
6075
6076     /**
6077      * Abort the current server request.
6078      */
6079     abort : function(){
6080         if(this.isLoading()){
6081             this.destroyTrans(this.trans);
6082         }
6083     },
6084
6085     // private
6086     destroyTrans : function(trans, isLoaded){
6087         this.head.removeChild(document.getElementById(trans.scriptId));
6088         clearTimeout(trans.timeoutId);
6089         if(isLoaded){
6090             window[trans.cb] = undefined;
6091             try{
6092                 delete window[trans.cb];
6093             }catch(e){}
6094         }else{
6095             // if hasn't been loaded, wait for load to remove it to prevent script error
6096             window[trans.cb] = function(){
6097                 window[trans.cb] = undefined;
6098                 try{
6099                     delete window[trans.cb];
6100                 }catch(e){}
6101             };
6102         }
6103     },
6104
6105     // private
6106     handleResponse : function(o, trans){
6107         this.trans = false;
6108         this.destroyTrans(trans, true);
6109         var result;
6110         try {
6111             result = trans.reader.readRecords(o);
6112         }catch(e){
6113             this.fireEvent("loadexception", this, o, trans.arg, e);
6114             trans.callback.call(trans.scope||window, null, trans.arg, false);
6115             return;
6116         }
6117         this.fireEvent("load", this, o, trans.arg);
6118         trans.callback.call(trans.scope||window, result, trans.arg, true);
6119     },
6120
6121     // private
6122     handleFailure : function(trans){
6123         this.trans = false;
6124         this.destroyTrans(trans, false);
6125         this.fireEvent("loadexception", this, null, trans.arg);
6126         trans.callback.call(trans.scope||window, null, trans.arg, false);
6127     }
6128 });/*
6129  * Based on:
6130  * Ext JS Library 1.1.1
6131  * Copyright(c) 2006-2007, Ext JS, LLC.
6132  *
6133  * Originally Released Under LGPL - original licence link has changed is not relivant.
6134  *
6135  * Fork - LGPL
6136  * <script type="text/javascript">
6137  */
6138
6139 /**
6140  * @class Roo.data.JsonReader
6141  * @extends Roo.data.DataReader
6142  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6143  * based on mappings in a provided Roo.data.Record constructor.
6144  * 
6145  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6146  * in the reply previously. 
6147  * 
6148  * <p>
6149  * Example code:
6150  * <pre><code>
6151 var RecordDef = Roo.data.Record.create([
6152     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6153     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6154 ]);
6155 var myReader = new Roo.data.JsonReader({
6156     totalProperty: "results",    // The property which contains the total dataset size (optional)
6157     root: "rows",                // The property which contains an Array of row objects
6158     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6159 }, RecordDef);
6160 </code></pre>
6161  * <p>
6162  * This would consume a JSON file like this:
6163  * <pre><code>
6164 { 'results': 2, 'rows': [
6165     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6166     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6167 }
6168 </code></pre>
6169  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6170  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6171  * paged from the remote server.
6172  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6173  * @cfg {String} root name of the property which contains the Array of row objects.
6174  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6175  * @cfg {Array} fields Array of field definition objects
6176  * @constructor
6177  * Create a new JsonReader
6178  * @param {Object} meta Metadata configuration options
6179  * @param {Object} recordType Either an Array of field definition objects,
6180  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6181  */
6182 Roo.data.JsonReader = function(meta, recordType){
6183     
6184     meta = meta || {};
6185     // set some defaults:
6186     Roo.applyIf(meta, {
6187         totalProperty: 'total',
6188         successProperty : 'success',
6189         root : 'data',
6190         id : 'id'
6191     });
6192     
6193     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6194 };
6195 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6196     
6197     /**
6198      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6199      * Used by Store query builder to append _requestMeta to params.
6200      * 
6201      */
6202     metaFromRemote : false,
6203     /**
6204      * This method is only used by a DataProxy which has retrieved data from a remote server.
6205      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6206      * @return {Object} data A data block which is used by an Roo.data.Store object as
6207      * a cache of Roo.data.Records.
6208      */
6209     read : function(response){
6210         var json = response.responseText;
6211        
6212         var o = /* eval:var:o */ eval("("+json+")");
6213         if(!o) {
6214             throw {message: "JsonReader.read: Json object not found"};
6215         }
6216         
6217         if(o.metaData){
6218             
6219             delete this.ef;
6220             this.metaFromRemote = true;
6221             this.meta = o.metaData;
6222             this.recordType = Roo.data.Record.create(o.metaData.fields);
6223             this.onMetaChange(this.meta, this.recordType, o);
6224         }
6225         return this.readRecords(o);
6226     },
6227
6228     // private function a store will implement
6229     onMetaChange : function(meta, recordType, o){
6230
6231     },
6232
6233     /**
6234          * @ignore
6235          */
6236     simpleAccess: function(obj, subsc) {
6237         return obj[subsc];
6238     },
6239
6240         /**
6241          * @ignore
6242          */
6243     getJsonAccessor: function(){
6244         var re = /[\[\.]/;
6245         return function(expr) {
6246             try {
6247                 return(re.test(expr))
6248                     ? new Function("obj", "return obj." + expr)
6249                     : function(obj){
6250                         return obj[expr];
6251                     };
6252             } catch(e){}
6253             return Roo.emptyFn;
6254         };
6255     }(),
6256
6257     /**
6258      * Create a data block containing Roo.data.Records from an XML document.
6259      * @param {Object} o An object which contains an Array of row objects in the property specified
6260      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6261      * which contains the total size of the dataset.
6262      * @return {Object} data A data block which is used by an Roo.data.Store object as
6263      * a cache of Roo.data.Records.
6264      */
6265     readRecords : function(o){
6266         /**
6267          * After any data loads, the raw JSON data is available for further custom processing.
6268          * @type Object
6269          */
6270         this.o = o;
6271         var s = this.meta, Record = this.recordType,
6272             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
6273
6274 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6275         if (!this.ef) {
6276             if(s.totalProperty) {
6277                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6278                 }
6279                 if(s.successProperty) {
6280                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6281                 }
6282                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6283                 if (s.id) {
6284                         var g = this.getJsonAccessor(s.id);
6285                         this.getId = function(rec) {
6286                                 var r = g(rec);  
6287                                 return (r === undefined || r === "") ? null : r;
6288                         };
6289                 } else {
6290                         this.getId = function(){return null;};
6291                 }
6292             this.ef = [];
6293             for(var jj = 0; jj < fl; jj++){
6294                 f = fi[jj];
6295                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6296                 this.ef[jj] = this.getJsonAccessor(map);
6297             }
6298         }
6299
6300         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6301         if(s.totalProperty){
6302             var vt = parseInt(this.getTotal(o), 10);
6303             if(!isNaN(vt)){
6304                 totalRecords = vt;
6305             }
6306         }
6307         if(s.successProperty){
6308             var vs = this.getSuccess(o);
6309             if(vs === false || vs === 'false'){
6310                 success = false;
6311             }
6312         }
6313         var records = [];
6314         for(var i = 0; i < c; i++){
6315                 var n = root[i];
6316             var values = {};
6317             var id = this.getId(n);
6318             for(var j = 0; j < fl; j++){
6319                 f = fi[j];
6320             var v = this.ef[j](n);
6321             if (!f.convert) {
6322                 Roo.log('missing convert for ' + f.name);
6323                 Roo.log(f);
6324                 continue;
6325             }
6326             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6327             }
6328             var record = new Record(values, id);
6329             record.json = n;
6330             records[i] = record;
6331         }
6332         return {
6333             raw : o,
6334             success : success,
6335             records : records,
6336             totalRecords : totalRecords
6337         };
6338     }
6339 });/*
6340  * Based on:
6341  * Ext JS Library 1.1.1
6342  * Copyright(c) 2006-2007, Ext JS, LLC.
6343  *
6344  * Originally Released Under LGPL - original licence link has changed is not relivant.
6345  *
6346  * Fork - LGPL
6347  * <script type="text/javascript">
6348  */
6349
6350 /**
6351  * @class Roo.data.XmlReader
6352  * @extends Roo.data.DataReader
6353  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6354  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6355  * <p>
6356  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6357  * header in the HTTP response must be set to "text/xml".</em>
6358  * <p>
6359  * Example code:
6360  * <pre><code>
6361 var RecordDef = Roo.data.Record.create([
6362    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6363    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6364 ]);
6365 var myReader = new Roo.data.XmlReader({
6366    totalRecords: "results", // The element which contains the total dataset size (optional)
6367    record: "row",           // The repeated element which contains row information
6368    id: "id"                 // The element within the row that provides an ID for the record (optional)
6369 }, RecordDef);
6370 </code></pre>
6371  * <p>
6372  * This would consume an XML file like this:
6373  * <pre><code>
6374 &lt;?xml?>
6375 &lt;dataset>
6376  &lt;results>2&lt;/results>
6377  &lt;row>
6378    &lt;id>1&lt;/id>
6379    &lt;name>Bill&lt;/name>
6380    &lt;occupation>Gardener&lt;/occupation>
6381  &lt;/row>
6382  &lt;row>
6383    &lt;id>2&lt;/id>
6384    &lt;name>Ben&lt;/name>
6385    &lt;occupation>Horticulturalist&lt;/occupation>
6386  &lt;/row>
6387 &lt;/dataset>
6388 </code></pre>
6389  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6390  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6391  * paged from the remote server.
6392  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6393  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6394  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6395  * a record identifier value.
6396  * @constructor
6397  * Create a new XmlReader
6398  * @param {Object} meta Metadata configuration options
6399  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6400  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6401  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6402  */
6403 Roo.data.XmlReader = function(meta, recordType){
6404     meta = meta || {};
6405     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6406 };
6407 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6408     /**
6409      * This method is only used by a DataProxy which has retrieved data from a remote server.
6410          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6411          * to contain a method called 'responseXML' that returns an XML document object.
6412      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6413      * a cache of Roo.data.Records.
6414      */
6415     read : function(response){
6416         var doc = response.responseXML;
6417         if(!doc) {
6418             throw {message: "XmlReader.read: XML Document not available"};
6419         }
6420         return this.readRecords(doc);
6421     },
6422
6423     /**
6424      * Create a data block containing Roo.data.Records from an XML document.
6425          * @param {Object} doc A parsed XML document.
6426      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6427      * a cache of Roo.data.Records.
6428      */
6429     readRecords : function(doc){
6430         /**
6431          * After any data loads/reads, the raw XML Document is available for further custom processing.
6432          * @type XMLDocument
6433          */
6434         this.xmlData = doc;
6435         var root = doc.documentElement || doc;
6436         var q = Roo.DomQuery;
6437         var recordType = this.recordType, fields = recordType.prototype.fields;
6438         var sid = this.meta.id;
6439         var totalRecords = 0, success = true;
6440         if(this.meta.totalRecords){
6441             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6442         }
6443         
6444         if(this.meta.success){
6445             var sv = q.selectValue(this.meta.success, root, true);
6446             success = sv !== false && sv !== 'false';
6447         }
6448         var records = [];
6449         var ns = q.select(this.meta.record, root);
6450         for(var i = 0, len = ns.length; i < len; i++) {
6451                 var n = ns[i];
6452                 var values = {};
6453                 var id = sid ? q.selectValue(sid, n) : undefined;
6454                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6455                     var f = fields.items[j];
6456                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6457                     v = f.convert(v);
6458                     values[f.name] = v;
6459                 }
6460                 var record = new recordType(values, id);
6461                 record.node = n;
6462                 records[records.length] = record;
6463             }
6464
6465             return {
6466                 success : success,
6467                 records : records,
6468                 totalRecords : totalRecords || records.length
6469             };
6470     }
6471 });/*
6472  * Based on:
6473  * Ext JS Library 1.1.1
6474  * Copyright(c) 2006-2007, Ext JS, LLC.
6475  *
6476  * Originally Released Under LGPL - original licence link has changed is not relivant.
6477  *
6478  * Fork - LGPL
6479  * <script type="text/javascript">
6480  */
6481
6482 /**
6483  * @class Roo.data.ArrayReader
6484  * @extends Roo.data.DataReader
6485  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6486  * Each element of that Array represents a row of data fields. The
6487  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6488  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6489  * <p>
6490  * Example code:.
6491  * <pre><code>
6492 var RecordDef = Roo.data.Record.create([
6493     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6494     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6495 ]);
6496 var myReader = new Roo.data.ArrayReader({
6497     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6498 }, RecordDef);
6499 </code></pre>
6500  * <p>
6501  * This would consume an Array like this:
6502  * <pre><code>
6503 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6504   </code></pre>
6505  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6506  * @constructor
6507  * Create a new JsonReader
6508  * @param {Object} meta Metadata configuration options.
6509  * @param {Object} recordType Either an Array of field definition objects
6510  * as specified to {@link Roo.data.Record#create},
6511  * or an {@link Roo.data.Record} object
6512  * created using {@link Roo.data.Record#create}.
6513  */
6514 Roo.data.ArrayReader = function(meta, recordType){
6515     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6516 };
6517
6518 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6519     /**
6520      * Create a data block containing Roo.data.Records from an XML document.
6521      * @param {Object} o An Array of row objects which represents the dataset.
6522      * @return {Object} data A data block which is used by an Roo.data.Store object as
6523      * a cache of Roo.data.Records.
6524      */
6525     readRecords : function(o){
6526         var sid = this.meta ? this.meta.id : null;
6527         var recordType = this.recordType, fields = recordType.prototype.fields;
6528         var records = [];
6529         var root = o;
6530             for(var i = 0; i < root.length; i++){
6531                     var n = root[i];
6532                 var values = {};
6533                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6534                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6535                 var f = fields.items[j];
6536                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6537                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6538                 v = f.convert(v);
6539                 values[f.name] = v;
6540             }
6541                 var record = new recordType(values, id);
6542                 record.json = n;
6543                 records[records.length] = record;
6544             }
6545             return {
6546                 records : records,
6547                 totalRecords : records.length
6548             };
6549     }
6550 });/*
6551  * Based on:
6552  * Ext JS Library 1.1.1
6553  * Copyright(c) 2006-2007, Ext JS, LLC.
6554  *
6555  * Originally Released Under LGPL - original licence link has changed is not relivant.
6556  *
6557  * Fork - LGPL
6558  * <script type="text/javascript">
6559  */
6560
6561
6562 /**
6563  * @class Roo.data.Tree
6564  * @extends Roo.util.Observable
6565  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6566  * in the tree have most standard DOM functionality.
6567  * @constructor
6568  * @param {Node} root (optional) The root node
6569  */
6570 Roo.data.Tree = function(root){
6571    this.nodeHash = {};
6572    /**
6573     * The root node for this tree
6574     * @type Node
6575     */
6576    this.root = null;
6577    if(root){
6578        this.setRootNode(root);
6579    }
6580    this.addEvents({
6581        /**
6582         * @event append
6583         * Fires when a new child node is appended to a node in this tree.
6584         * @param {Tree} tree The owner tree
6585         * @param {Node} parent The parent node
6586         * @param {Node} node The newly appended node
6587         * @param {Number} index The index of the newly appended node
6588         */
6589        "append" : true,
6590        /**
6591         * @event remove
6592         * Fires when a child node is removed from a node in this tree.
6593         * @param {Tree} tree The owner tree
6594         * @param {Node} parent The parent node
6595         * @param {Node} node The child node removed
6596         */
6597        "remove" : true,
6598        /**
6599         * @event move
6600         * Fires when a node is moved to a new location in the tree
6601         * @param {Tree} tree The owner tree
6602         * @param {Node} node The node moved
6603         * @param {Node} oldParent The old parent of this node
6604         * @param {Node} newParent The new parent of this node
6605         * @param {Number} index The index it was moved to
6606         */
6607        "move" : true,
6608        /**
6609         * @event insert
6610         * Fires when a new child node is inserted in a node in this tree.
6611         * @param {Tree} tree The owner tree
6612         * @param {Node} parent The parent node
6613         * @param {Node} node The child node inserted
6614         * @param {Node} refNode The child node the node was inserted before
6615         */
6616        "insert" : true,
6617        /**
6618         * @event beforeappend
6619         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6620         * @param {Tree} tree The owner tree
6621         * @param {Node} parent The parent node
6622         * @param {Node} node The child node to be appended
6623         */
6624        "beforeappend" : true,
6625        /**
6626         * @event beforeremove
6627         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6628         * @param {Tree} tree The owner tree
6629         * @param {Node} parent The parent node
6630         * @param {Node} node The child node to be removed
6631         */
6632        "beforeremove" : true,
6633        /**
6634         * @event beforemove
6635         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6636         * @param {Tree} tree The owner tree
6637         * @param {Node} node The node being moved
6638         * @param {Node} oldParent The parent of the node
6639         * @param {Node} newParent The new parent the node is moving to
6640         * @param {Number} index The index it is being moved to
6641         */
6642        "beforemove" : true,
6643        /**
6644         * @event beforeinsert
6645         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6646         * @param {Tree} tree The owner tree
6647         * @param {Node} parent The parent node
6648         * @param {Node} node The child node to be inserted
6649         * @param {Node} refNode The child node the node is being inserted before
6650         */
6651        "beforeinsert" : true
6652    });
6653
6654     Roo.data.Tree.superclass.constructor.call(this);
6655 };
6656
6657 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6658     pathSeparator: "/",
6659
6660     proxyNodeEvent : function(){
6661         return this.fireEvent.apply(this, arguments);
6662     },
6663
6664     /**
6665      * Returns the root node for this tree.
6666      * @return {Node}
6667      */
6668     getRootNode : function(){
6669         return this.root;
6670     },
6671
6672     /**
6673      * Sets the root node for this tree.
6674      * @param {Node} node
6675      * @return {Node}
6676      */
6677     setRootNode : function(node){
6678         this.root = node;
6679         node.ownerTree = this;
6680         node.isRoot = true;
6681         this.registerNode(node);
6682         return node;
6683     },
6684
6685     /**
6686      * Gets a node in this tree by its id.
6687      * @param {String} id
6688      * @return {Node}
6689      */
6690     getNodeById : function(id){
6691         return this.nodeHash[id];
6692     },
6693
6694     registerNode : function(node){
6695         this.nodeHash[node.id] = node;
6696     },
6697
6698     unregisterNode : function(node){
6699         delete this.nodeHash[node.id];
6700     },
6701
6702     toString : function(){
6703         return "[Tree"+(this.id?" "+this.id:"")+"]";
6704     }
6705 });
6706
6707 /**
6708  * @class Roo.data.Node
6709  * @extends Roo.util.Observable
6710  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6711  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6712  * @constructor
6713  * @param {Object} attributes The attributes/config for the node
6714  */
6715 Roo.data.Node = function(attributes){
6716     /**
6717      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6718      * @type {Object}
6719      */
6720     this.attributes = attributes || {};
6721     this.leaf = this.attributes.leaf;
6722     /**
6723      * The node id. @type String
6724      */
6725     this.id = this.attributes.id;
6726     if(!this.id){
6727         this.id = Roo.id(null, "ynode-");
6728         this.attributes.id = this.id;
6729     }
6730      
6731     
6732     /**
6733      * All child nodes of this node. @type Array
6734      */
6735     this.childNodes = [];
6736     if(!this.childNodes.indexOf){ // indexOf is a must
6737         this.childNodes.indexOf = function(o){
6738             for(var i = 0, len = this.length; i < len; i++){
6739                 if(this[i] == o) {
6740                     return i;
6741                 }
6742             }
6743             return -1;
6744         };
6745     }
6746     /**
6747      * The parent node for this node. @type Node
6748      */
6749     this.parentNode = null;
6750     /**
6751      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6752      */
6753     this.firstChild = null;
6754     /**
6755      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6756      */
6757     this.lastChild = null;
6758     /**
6759      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6760      */
6761     this.previousSibling = null;
6762     /**
6763      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6764      */
6765     this.nextSibling = null;
6766
6767     this.addEvents({
6768        /**
6769         * @event append
6770         * Fires when a new child node is appended
6771         * @param {Tree} tree The owner tree
6772         * @param {Node} this This node
6773         * @param {Node} node The newly appended node
6774         * @param {Number} index The index of the newly appended node
6775         */
6776        "append" : true,
6777        /**
6778         * @event remove
6779         * Fires when a child node is removed
6780         * @param {Tree} tree The owner tree
6781         * @param {Node} this This node
6782         * @param {Node} node The removed node
6783         */
6784        "remove" : true,
6785        /**
6786         * @event move
6787         * Fires when this node is moved to a new location in the tree
6788         * @param {Tree} tree The owner tree
6789         * @param {Node} this This node
6790         * @param {Node} oldParent The old parent of this node
6791         * @param {Node} newParent The new parent of this node
6792         * @param {Number} index The index it was moved to
6793         */
6794        "move" : true,
6795        /**
6796         * @event insert
6797         * Fires when a new child node is inserted.
6798         * @param {Tree} tree The owner tree
6799         * @param {Node} this This node
6800         * @param {Node} node The child node inserted
6801         * @param {Node} refNode The child node the node was inserted before
6802         */
6803        "insert" : true,
6804        /**
6805         * @event beforeappend
6806         * Fires before a new child is appended, return false to cancel the append.
6807         * @param {Tree} tree The owner tree
6808         * @param {Node} this This node
6809         * @param {Node} node The child node to be appended
6810         */
6811        "beforeappend" : true,
6812        /**
6813         * @event beforeremove
6814         * Fires before a child is removed, return false to cancel the remove.
6815         * @param {Tree} tree The owner tree
6816         * @param {Node} this This node
6817         * @param {Node} node The child node to be removed
6818         */
6819        "beforeremove" : true,
6820        /**
6821         * @event beforemove
6822         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6823         * @param {Tree} tree The owner tree
6824         * @param {Node} this This node
6825         * @param {Node} oldParent The parent of this node
6826         * @param {Node} newParent The new parent this node is moving to
6827         * @param {Number} index The index it is being moved to
6828         */
6829        "beforemove" : true,
6830        /**
6831         * @event beforeinsert
6832         * Fires before a new child is inserted, return false to cancel the insert.
6833         * @param {Tree} tree The owner tree
6834         * @param {Node} this This node
6835         * @param {Node} node The child node to be inserted
6836         * @param {Node} refNode The child node the node is being inserted before
6837         */
6838        "beforeinsert" : true
6839    });
6840     this.listeners = this.attributes.listeners;
6841     Roo.data.Node.superclass.constructor.call(this);
6842 };
6843
6844 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6845     fireEvent : function(evtName){
6846         // first do standard event for this node
6847         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6848             return false;
6849         }
6850         // then bubble it up to the tree if the event wasn't cancelled
6851         var ot = this.getOwnerTree();
6852         if(ot){
6853             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6854                 return false;
6855             }
6856         }
6857         return true;
6858     },
6859
6860     /**
6861      * Returns true if this node is a leaf
6862      * @return {Boolean}
6863      */
6864     isLeaf : function(){
6865         return this.leaf === true;
6866     },
6867
6868     // private
6869     setFirstChild : function(node){
6870         this.firstChild = node;
6871     },
6872
6873     //private
6874     setLastChild : function(node){
6875         this.lastChild = node;
6876     },
6877
6878
6879     /**
6880      * Returns true if this node is the last child of its parent
6881      * @return {Boolean}
6882      */
6883     isLast : function(){
6884        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6885     },
6886
6887     /**
6888      * Returns true if this node is the first child of its parent
6889      * @return {Boolean}
6890      */
6891     isFirst : function(){
6892        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6893     },
6894
6895     hasChildNodes : function(){
6896         return !this.isLeaf() && this.childNodes.length > 0;
6897     },
6898
6899     /**
6900      * Insert node(s) as the last child node of this node.
6901      * @param {Node/Array} node The node or Array of nodes to append
6902      * @return {Node} The appended node if single append, or null if an array was passed
6903      */
6904     appendChild : function(node){
6905         var multi = false;
6906         if(node instanceof Array){
6907             multi = node;
6908         }else if(arguments.length > 1){
6909             multi = arguments;
6910         }
6911         // if passed an array or multiple args do them one by one
6912         if(multi){
6913             for(var i = 0, len = multi.length; i < len; i++) {
6914                 this.appendChild(multi[i]);
6915             }
6916         }else{
6917             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6918                 return false;
6919             }
6920             var index = this.childNodes.length;
6921             var oldParent = node.parentNode;
6922             // it's a move, make sure we move it cleanly
6923             if(oldParent){
6924                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6925                     return false;
6926                 }
6927                 oldParent.removeChild(node);
6928             }
6929             index = this.childNodes.length;
6930             if(index == 0){
6931                 this.setFirstChild(node);
6932             }
6933             this.childNodes.push(node);
6934             node.parentNode = this;
6935             var ps = this.childNodes[index-1];
6936             if(ps){
6937                 node.previousSibling = ps;
6938                 ps.nextSibling = node;
6939             }else{
6940                 node.previousSibling = null;
6941             }
6942             node.nextSibling = null;
6943             this.setLastChild(node);
6944             node.setOwnerTree(this.getOwnerTree());
6945             this.fireEvent("append", this.ownerTree, this, node, index);
6946             if(oldParent){
6947                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6948             }
6949             return node;
6950         }
6951     },
6952
6953     /**
6954      * Removes a child node from this node.
6955      * @param {Node} node The node to remove
6956      * @return {Node} The removed node
6957      */
6958     removeChild : function(node){
6959         var index = this.childNodes.indexOf(node);
6960         if(index == -1){
6961             return false;
6962         }
6963         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6964             return false;
6965         }
6966
6967         // remove it from childNodes collection
6968         this.childNodes.splice(index, 1);
6969
6970         // update siblings
6971         if(node.previousSibling){
6972             node.previousSibling.nextSibling = node.nextSibling;
6973         }
6974         if(node.nextSibling){
6975             node.nextSibling.previousSibling = node.previousSibling;
6976         }
6977
6978         // update child refs
6979         if(this.firstChild == node){
6980             this.setFirstChild(node.nextSibling);
6981         }
6982         if(this.lastChild == node){
6983             this.setLastChild(node.previousSibling);
6984         }
6985
6986         node.setOwnerTree(null);
6987         // clear any references from the node
6988         node.parentNode = null;
6989         node.previousSibling = null;
6990         node.nextSibling = null;
6991         this.fireEvent("remove", this.ownerTree, this, node);
6992         return node;
6993     },
6994
6995     /**
6996      * Inserts the first node before the second node in this nodes childNodes collection.
6997      * @param {Node} node The node to insert
6998      * @param {Node} refNode The node to insert before (if null the node is appended)
6999      * @return {Node} The inserted node
7000      */
7001     insertBefore : function(node, refNode){
7002         if(!refNode){ // like standard Dom, refNode can be null for append
7003             return this.appendChild(node);
7004         }
7005         // nothing to do
7006         if(node == refNode){
7007             return false;
7008         }
7009
7010         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
7011             return false;
7012         }
7013         var index = this.childNodes.indexOf(refNode);
7014         var oldParent = node.parentNode;
7015         var refIndex = index;
7016
7017         // when moving internally, indexes will change after remove
7018         if(oldParent == this && this.childNodes.indexOf(node) < index){
7019             refIndex--;
7020         }
7021
7022         // it's a move, make sure we move it cleanly
7023         if(oldParent){
7024             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
7025                 return false;
7026             }
7027             oldParent.removeChild(node);
7028         }
7029         if(refIndex == 0){
7030             this.setFirstChild(node);
7031         }
7032         this.childNodes.splice(refIndex, 0, node);
7033         node.parentNode = this;
7034         var ps = this.childNodes[refIndex-1];
7035         if(ps){
7036             node.previousSibling = ps;
7037             ps.nextSibling = node;
7038         }else{
7039             node.previousSibling = null;
7040         }
7041         node.nextSibling = refNode;
7042         refNode.previousSibling = node;
7043         node.setOwnerTree(this.getOwnerTree());
7044         this.fireEvent("insert", this.ownerTree, this, node, refNode);
7045         if(oldParent){
7046             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
7047         }
7048         return node;
7049     },
7050
7051     /**
7052      * Returns the child node at the specified index.
7053      * @param {Number} index
7054      * @return {Node}
7055      */
7056     item : function(index){
7057         return this.childNodes[index];
7058     },
7059
7060     /**
7061      * Replaces one child node in this node with another.
7062      * @param {Node} newChild The replacement node
7063      * @param {Node} oldChild The node to replace
7064      * @return {Node} The replaced node
7065      */
7066     replaceChild : function(newChild, oldChild){
7067         this.insertBefore(newChild, oldChild);
7068         this.removeChild(oldChild);
7069         return oldChild;
7070     },
7071
7072     /**
7073      * Returns the index of a child node
7074      * @param {Node} node
7075      * @return {Number} The index of the node or -1 if it was not found
7076      */
7077     indexOf : function(child){
7078         return this.childNodes.indexOf(child);
7079     },
7080
7081     /**
7082      * Returns the tree this node is in.
7083      * @return {Tree}
7084      */
7085     getOwnerTree : function(){
7086         // if it doesn't have one, look for one
7087         if(!this.ownerTree){
7088             var p = this;
7089             while(p){
7090                 if(p.ownerTree){
7091                     this.ownerTree = p.ownerTree;
7092                     break;
7093                 }
7094                 p = p.parentNode;
7095             }
7096         }
7097         return this.ownerTree;
7098     },
7099
7100     /**
7101      * Returns depth of this node (the root node has a depth of 0)
7102      * @return {Number}
7103      */
7104     getDepth : function(){
7105         var depth = 0;
7106         var p = this;
7107         while(p.parentNode){
7108             ++depth;
7109             p = p.parentNode;
7110         }
7111         return depth;
7112     },
7113
7114     // private
7115     setOwnerTree : function(tree){
7116         // if it's move, we need to update everyone
7117         if(tree != this.ownerTree){
7118             if(this.ownerTree){
7119                 this.ownerTree.unregisterNode(this);
7120             }
7121             this.ownerTree = tree;
7122             var cs = this.childNodes;
7123             for(var i = 0, len = cs.length; i < len; i++) {
7124                 cs[i].setOwnerTree(tree);
7125             }
7126             if(tree){
7127                 tree.registerNode(this);
7128             }
7129         }
7130     },
7131
7132     /**
7133      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7134      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7135      * @return {String} The path
7136      */
7137     getPath : function(attr){
7138         attr = attr || "id";
7139         var p = this.parentNode;
7140         var b = [this.attributes[attr]];
7141         while(p){
7142             b.unshift(p.attributes[attr]);
7143             p = p.parentNode;
7144         }
7145         var sep = this.getOwnerTree().pathSeparator;
7146         return sep + b.join(sep);
7147     },
7148
7149     /**
7150      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7151      * function call will be the scope provided or the current node. The arguments to the function
7152      * will be the args provided or the current node. If the function returns false at any point,
7153      * the bubble is stopped.
7154      * @param {Function} fn The function to call
7155      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7156      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7157      */
7158     bubble : function(fn, scope, args){
7159         var p = this;
7160         while(p){
7161             if(fn.call(scope || p, args || p) === false){
7162                 break;
7163             }
7164             p = p.parentNode;
7165         }
7166     },
7167
7168     /**
7169      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7170      * function call will be the scope provided or the current node. The arguments to the function
7171      * will be the args provided or the current node. If the function returns false at any point,
7172      * the cascade is stopped on that branch.
7173      * @param {Function} fn The function to call
7174      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7175      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7176      */
7177     cascade : function(fn, scope, args){
7178         if(fn.call(scope || this, args || this) !== false){
7179             var cs = this.childNodes;
7180             for(var i = 0, len = cs.length; i < len; i++) {
7181                 cs[i].cascade(fn, scope, args);
7182             }
7183         }
7184     },
7185
7186     /**
7187      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7188      * function call will be the scope provided or the current node. The arguments to the function
7189      * will be the args provided or the current node. If the function returns false at any point,
7190      * the iteration stops.
7191      * @param {Function} fn The function to call
7192      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7193      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7194      */
7195     eachChild : function(fn, scope, args){
7196         var cs = this.childNodes;
7197         for(var i = 0, len = cs.length; i < len; i++) {
7198                 if(fn.call(scope || this, args || cs[i]) === false){
7199                     break;
7200                 }
7201         }
7202     },
7203
7204     /**
7205      * Finds the first child that has the attribute with the specified value.
7206      * @param {String} attribute The attribute name
7207      * @param {Mixed} value The value to search for
7208      * @return {Node} The found child or null if none was found
7209      */
7210     findChild : function(attribute, value){
7211         var cs = this.childNodes;
7212         for(var i = 0, len = cs.length; i < len; i++) {
7213                 if(cs[i].attributes[attribute] == value){
7214                     return cs[i];
7215                 }
7216         }
7217         return null;
7218     },
7219
7220     /**
7221      * Finds the first child by a custom function. The child matches if the function passed
7222      * returns true.
7223      * @param {Function} fn
7224      * @param {Object} scope (optional)
7225      * @return {Node} The found child or null if none was found
7226      */
7227     findChildBy : function(fn, scope){
7228         var cs = this.childNodes;
7229         for(var i = 0, len = cs.length; i < len; i++) {
7230                 if(fn.call(scope||cs[i], cs[i]) === true){
7231                     return cs[i];
7232                 }
7233         }
7234         return null;
7235     },
7236
7237     /**
7238      * Sorts this nodes children using the supplied sort function
7239      * @param {Function} fn
7240      * @param {Object} scope (optional)
7241      */
7242     sort : function(fn, scope){
7243         var cs = this.childNodes;
7244         var len = cs.length;
7245         if(len > 0){
7246             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7247             cs.sort(sortFn);
7248             for(var i = 0; i < len; i++){
7249                 var n = cs[i];
7250                 n.previousSibling = cs[i-1];
7251                 n.nextSibling = cs[i+1];
7252                 if(i == 0){
7253                     this.setFirstChild(n);
7254                 }
7255                 if(i == len-1){
7256                     this.setLastChild(n);
7257                 }
7258             }
7259         }
7260     },
7261
7262     /**
7263      * Returns true if this node is an ancestor (at any point) of the passed node.
7264      * @param {Node} node
7265      * @return {Boolean}
7266      */
7267     contains : function(node){
7268         return node.isAncestor(this);
7269     },
7270
7271     /**
7272      * Returns true if the passed node is an ancestor (at any point) of this node.
7273      * @param {Node} node
7274      * @return {Boolean}
7275      */
7276     isAncestor : function(node){
7277         var p = this.parentNode;
7278         while(p){
7279             if(p == node){
7280                 return true;
7281             }
7282             p = p.parentNode;
7283         }
7284         return false;
7285     },
7286
7287     toString : function(){
7288         return "[Node"+(this.id?" "+this.id:"")+"]";
7289     }
7290 });/*
7291  * Based on:
7292  * Ext JS Library 1.1.1
7293  * Copyright(c) 2006-2007, Ext JS, LLC.
7294  *
7295  * Originally Released Under LGPL - original licence link has changed is not relivant.
7296  *
7297  * Fork - LGPL
7298  * <script type="text/javascript">
7299  */
7300  (function(){ 
7301 /**
7302  * @class Roo.Layer
7303  * @extends Roo.Element
7304  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7305  * automatic maintaining of shadow/shim positions.
7306  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7307  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7308  * you can pass a string with a CSS class name. False turns off the shadow.
7309  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7310  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7311  * @cfg {String} cls CSS class to add to the element
7312  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7313  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7314  * @constructor
7315  * @param {Object} config An object with config options.
7316  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7317  */
7318
7319 Roo.Layer = function(config, existingEl){
7320     config = config || {};
7321     var dh = Roo.DomHelper;
7322     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7323     if(existingEl){
7324         this.dom = Roo.getDom(existingEl);
7325     }
7326     if(!this.dom){
7327         var o = config.dh || {tag: "div", cls: "x-layer"};
7328         this.dom = dh.append(pel, o);
7329     }
7330     if(config.cls){
7331         this.addClass(config.cls);
7332     }
7333     this.constrain = config.constrain !== false;
7334     this.visibilityMode = Roo.Element.VISIBILITY;
7335     if(config.id){
7336         this.id = this.dom.id = config.id;
7337     }else{
7338         this.id = Roo.id(this.dom);
7339     }
7340     this.zindex = config.zindex || this.getZIndex();
7341     this.position("absolute", this.zindex);
7342     if(config.shadow){
7343         this.shadowOffset = config.shadowOffset || 4;
7344         this.shadow = new Roo.Shadow({
7345             offset : this.shadowOffset,
7346             mode : config.shadow
7347         });
7348     }else{
7349         this.shadowOffset = 0;
7350     }
7351     this.useShim = config.shim !== false && Roo.useShims;
7352     this.useDisplay = config.useDisplay;
7353     this.hide();
7354 };
7355
7356 var supr = Roo.Element.prototype;
7357
7358 // shims are shared among layer to keep from having 100 iframes
7359 var shims = [];
7360
7361 Roo.extend(Roo.Layer, Roo.Element, {
7362
7363     getZIndex : function(){
7364         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7365     },
7366
7367     getShim : function(){
7368         if(!this.useShim){
7369             return null;
7370         }
7371         if(this.shim){
7372             return this.shim;
7373         }
7374         var shim = shims.shift();
7375         if(!shim){
7376             shim = this.createShim();
7377             shim.enableDisplayMode('block');
7378             shim.dom.style.display = 'none';
7379             shim.dom.style.visibility = 'visible';
7380         }
7381         var pn = this.dom.parentNode;
7382         if(shim.dom.parentNode != pn){
7383             pn.insertBefore(shim.dom, this.dom);
7384         }
7385         shim.setStyle('z-index', this.getZIndex()-2);
7386         this.shim = shim;
7387         return shim;
7388     },
7389
7390     hideShim : function(){
7391         if(this.shim){
7392             this.shim.setDisplayed(false);
7393             shims.push(this.shim);
7394             delete this.shim;
7395         }
7396     },
7397
7398     disableShadow : function(){
7399         if(this.shadow){
7400             this.shadowDisabled = true;
7401             this.shadow.hide();
7402             this.lastShadowOffset = this.shadowOffset;
7403             this.shadowOffset = 0;
7404         }
7405     },
7406
7407     enableShadow : function(show){
7408         if(this.shadow){
7409             this.shadowDisabled = false;
7410             this.shadowOffset = this.lastShadowOffset;
7411             delete this.lastShadowOffset;
7412             if(show){
7413                 this.sync(true);
7414             }
7415         }
7416     },
7417
7418     // private
7419     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7420     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7421     sync : function(doShow){
7422         var sw = this.shadow;
7423         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7424             var sh = this.getShim();
7425
7426             var w = this.getWidth(),
7427                 h = this.getHeight();
7428
7429             var l = this.getLeft(true),
7430                 t = this.getTop(true);
7431
7432             if(sw && !this.shadowDisabled){
7433                 if(doShow && !sw.isVisible()){
7434                     sw.show(this);
7435                 }else{
7436                     sw.realign(l, t, w, h);
7437                 }
7438                 if(sh){
7439                     if(doShow){
7440                        sh.show();
7441                     }
7442                     // fit the shim behind the shadow, so it is shimmed too
7443                     var a = sw.adjusts, s = sh.dom.style;
7444                     s.left = (Math.min(l, l+a.l))+"px";
7445                     s.top = (Math.min(t, t+a.t))+"px";
7446                     s.width = (w+a.w)+"px";
7447                     s.height = (h+a.h)+"px";
7448                 }
7449             }else if(sh){
7450                 if(doShow){
7451                    sh.show();
7452                 }
7453                 sh.setSize(w, h);
7454                 sh.setLeftTop(l, t);
7455             }
7456             
7457         }
7458     },
7459
7460     // private
7461     destroy : function(){
7462         this.hideShim();
7463         if(this.shadow){
7464             this.shadow.hide();
7465         }
7466         this.removeAllListeners();
7467         var pn = this.dom.parentNode;
7468         if(pn){
7469             pn.removeChild(this.dom);
7470         }
7471         Roo.Element.uncache(this.id);
7472     },
7473
7474     remove : function(){
7475         this.destroy();
7476     },
7477
7478     // private
7479     beginUpdate : function(){
7480         this.updating = true;
7481     },
7482
7483     // private
7484     endUpdate : function(){
7485         this.updating = false;
7486         this.sync(true);
7487     },
7488
7489     // private
7490     hideUnders : function(negOffset){
7491         if(this.shadow){
7492             this.shadow.hide();
7493         }
7494         this.hideShim();
7495     },
7496
7497     // private
7498     constrainXY : function(){
7499         if(this.constrain){
7500             var vw = Roo.lib.Dom.getViewWidth(),
7501                 vh = Roo.lib.Dom.getViewHeight();
7502             var s = Roo.get(document).getScroll();
7503
7504             var xy = this.getXY();
7505             var x = xy[0], y = xy[1];   
7506             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7507             // only move it if it needs it
7508             var moved = false;
7509             // first validate right/bottom
7510             if((x + w) > vw+s.left){
7511                 x = vw - w - this.shadowOffset;
7512                 moved = true;
7513             }
7514             if((y + h) > vh+s.top){
7515                 y = vh - h - this.shadowOffset;
7516                 moved = true;
7517             }
7518             // then make sure top/left isn't negative
7519             if(x < s.left){
7520                 x = s.left;
7521                 moved = true;
7522             }
7523             if(y < s.top){
7524                 y = s.top;
7525                 moved = true;
7526             }
7527             if(moved){
7528                 if(this.avoidY){
7529                     var ay = this.avoidY;
7530                     if(y <= ay && (y+h) >= ay){
7531                         y = ay-h-5;   
7532                     }
7533                 }
7534                 xy = [x, y];
7535                 this.storeXY(xy);
7536                 supr.setXY.call(this, xy);
7537                 this.sync();
7538             }
7539         }
7540     },
7541
7542     isVisible : function(){
7543         return this.visible;    
7544     },
7545
7546     // private
7547     showAction : function(){
7548         this.visible = true; // track visibility to prevent getStyle calls
7549         if(this.useDisplay === true){
7550             this.setDisplayed("");
7551         }else if(this.lastXY){
7552             supr.setXY.call(this, this.lastXY);
7553         }else if(this.lastLT){
7554             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7555         }
7556     },
7557
7558     // private
7559     hideAction : function(){
7560         this.visible = false;
7561         if(this.useDisplay === true){
7562             this.setDisplayed(false);
7563         }else{
7564             this.setLeftTop(-10000,-10000);
7565         }
7566     },
7567
7568     // overridden Element method
7569     setVisible : function(v, a, d, c, e){
7570         if(v){
7571             this.showAction();
7572         }
7573         if(a && v){
7574             var cb = function(){
7575                 this.sync(true);
7576                 if(c){
7577                     c();
7578                 }
7579             }.createDelegate(this);
7580             supr.setVisible.call(this, true, true, d, cb, e);
7581         }else{
7582             if(!v){
7583                 this.hideUnders(true);
7584             }
7585             var cb = c;
7586             if(a){
7587                 cb = function(){
7588                     this.hideAction();
7589                     if(c){
7590                         c();
7591                     }
7592                 }.createDelegate(this);
7593             }
7594             supr.setVisible.call(this, v, a, d, cb, e);
7595             if(v){
7596                 this.sync(true);
7597             }else if(!a){
7598                 this.hideAction();
7599             }
7600         }
7601     },
7602
7603     storeXY : function(xy){
7604         delete this.lastLT;
7605         this.lastXY = xy;
7606     },
7607
7608     storeLeftTop : function(left, top){
7609         delete this.lastXY;
7610         this.lastLT = [left, top];
7611     },
7612
7613     // private
7614     beforeFx : function(){
7615         this.beforeAction();
7616         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
7617     },
7618
7619     // private
7620     afterFx : function(){
7621         Roo.Layer.superclass.afterFx.apply(this, arguments);
7622         this.sync(this.isVisible());
7623     },
7624
7625     // private
7626     beforeAction : function(){
7627         if(!this.updating && this.shadow){
7628             this.shadow.hide();
7629         }
7630     },
7631
7632     // overridden Element method
7633     setLeft : function(left){
7634         this.storeLeftTop(left, this.getTop(true));
7635         supr.setLeft.apply(this, arguments);
7636         this.sync();
7637     },
7638
7639     setTop : function(top){
7640         this.storeLeftTop(this.getLeft(true), top);
7641         supr.setTop.apply(this, arguments);
7642         this.sync();
7643     },
7644
7645     setLeftTop : function(left, top){
7646         this.storeLeftTop(left, top);
7647         supr.setLeftTop.apply(this, arguments);
7648         this.sync();
7649     },
7650
7651     setXY : function(xy, a, d, c, e){
7652         this.fixDisplay();
7653         this.beforeAction();
7654         this.storeXY(xy);
7655         var cb = this.createCB(c);
7656         supr.setXY.call(this, xy, a, d, cb, e);
7657         if(!a){
7658             cb();
7659         }
7660     },
7661
7662     // private
7663     createCB : function(c){
7664         var el = this;
7665         return function(){
7666             el.constrainXY();
7667             el.sync(true);
7668             if(c){
7669                 c();
7670             }
7671         };
7672     },
7673
7674     // overridden Element method
7675     setX : function(x, a, d, c, e){
7676         this.setXY([x, this.getY()], a, d, c, e);
7677     },
7678
7679     // overridden Element method
7680     setY : function(y, a, d, c, e){
7681         this.setXY([this.getX(), y], a, d, c, e);
7682     },
7683
7684     // overridden Element method
7685     setSize : function(w, h, a, d, c, e){
7686         this.beforeAction();
7687         var cb = this.createCB(c);
7688         supr.setSize.call(this, w, h, a, d, cb, e);
7689         if(!a){
7690             cb();
7691         }
7692     },
7693
7694     // overridden Element method
7695     setWidth : function(w, a, d, c, e){
7696         this.beforeAction();
7697         var cb = this.createCB(c);
7698         supr.setWidth.call(this, w, a, d, cb, e);
7699         if(!a){
7700             cb();
7701         }
7702     },
7703
7704     // overridden Element method
7705     setHeight : function(h, a, d, c, e){
7706         this.beforeAction();
7707         var cb = this.createCB(c);
7708         supr.setHeight.call(this, h, a, d, cb, e);
7709         if(!a){
7710             cb();
7711         }
7712     },
7713
7714     // overridden Element method
7715     setBounds : function(x, y, w, h, a, d, c, e){
7716         this.beforeAction();
7717         var cb = this.createCB(c);
7718         if(!a){
7719             this.storeXY([x, y]);
7720             supr.setXY.call(this, [x, y]);
7721             supr.setSize.call(this, w, h, a, d, cb, e);
7722             cb();
7723         }else{
7724             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
7725         }
7726         return this;
7727     },
7728     
7729     /**
7730      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
7731      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
7732      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
7733      * @param {Number} zindex The new z-index to set
7734      * @return {this} The Layer
7735      */
7736     setZIndex : function(zindex){
7737         this.zindex = zindex;
7738         this.setStyle("z-index", zindex + 2);
7739         if(this.shadow){
7740             this.shadow.setZIndex(zindex + 1);
7741         }
7742         if(this.shim){
7743             this.shim.setStyle("z-index", zindex);
7744         }
7745     }
7746 });
7747 })();/*
7748  * Based on:
7749  * Ext JS Library 1.1.1
7750  * Copyright(c) 2006-2007, Ext JS, LLC.
7751  *
7752  * Originally Released Under LGPL - original licence link has changed is not relivant.
7753  *
7754  * Fork - LGPL
7755  * <script type="text/javascript">
7756  */
7757
7758
7759 /**
7760  * @class Roo.Shadow
7761  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
7762  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
7763  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
7764  * @constructor
7765  * Create a new Shadow
7766  * @param {Object} config The config object
7767  */
7768 Roo.Shadow = function(config){
7769     Roo.apply(this, config);
7770     if(typeof this.mode != "string"){
7771         this.mode = this.defaultMode;
7772     }
7773     var o = this.offset, a = {h: 0};
7774     var rad = Math.floor(this.offset/2);
7775     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
7776         case "drop":
7777             a.w = 0;
7778             a.l = a.t = o;
7779             a.t -= 1;
7780             if(Roo.isIE){
7781                 a.l -= this.offset + rad;
7782                 a.t -= this.offset + rad;
7783                 a.w -= rad;
7784                 a.h -= rad;
7785                 a.t += 1;
7786             }
7787         break;
7788         case "sides":
7789             a.w = (o*2);
7790             a.l = -o;
7791             a.t = o-1;
7792             if(Roo.isIE){
7793                 a.l -= (this.offset - rad);
7794                 a.t -= this.offset + rad;
7795                 a.l += 1;
7796                 a.w -= (this.offset - rad)*2;
7797                 a.w -= rad + 1;
7798                 a.h -= 1;
7799             }
7800         break;
7801         case "frame":
7802             a.w = a.h = (o*2);
7803             a.l = a.t = -o;
7804             a.t += 1;
7805             a.h -= 2;
7806             if(Roo.isIE){
7807                 a.l -= (this.offset - rad);
7808                 a.t -= (this.offset - rad);
7809                 a.l += 1;
7810                 a.w -= (this.offset + rad + 1);
7811                 a.h -= (this.offset + rad);
7812                 a.h += 1;
7813             }
7814         break;
7815     };
7816
7817     this.adjusts = a;
7818 };
7819
7820 Roo.Shadow.prototype = {
7821     /**
7822      * @cfg {String} mode
7823      * The shadow display mode.  Supports the following options:<br />
7824      * sides: Shadow displays on both sides and bottom only<br />
7825      * frame: Shadow displays equally on all four sides<br />
7826      * drop: Traditional bottom-right drop shadow (default)
7827      */
7828     /**
7829      * @cfg {String} offset
7830      * The number of pixels to offset the shadow from the element (defaults to 4)
7831      */
7832     offset: 4,
7833
7834     // private
7835     defaultMode: "drop",
7836
7837     /**
7838      * Displays the shadow under the target element
7839      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
7840      */
7841     show : function(target){
7842         target = Roo.get(target);
7843         if(!this.el){
7844             this.el = Roo.Shadow.Pool.pull();
7845             if(this.el.dom.nextSibling != target.dom){
7846                 this.el.insertBefore(target);
7847             }
7848         }
7849         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
7850         if(Roo.isIE){
7851             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
7852         }
7853         this.realign(
7854             target.getLeft(true),
7855             target.getTop(true),
7856             target.getWidth(),
7857             target.getHeight()
7858         );
7859         this.el.dom.style.display = "block";
7860     },
7861
7862     /**
7863      * Returns true if the shadow is visible, else false
7864      */
7865     isVisible : function(){
7866         return this.el ? true : false;  
7867     },
7868
7869     /**
7870      * Direct alignment when values are already available. Show must be called at least once before
7871      * calling this method to ensure it is initialized.
7872      * @param {Number} left The target element left position
7873      * @param {Number} top The target element top position
7874      * @param {Number} width The target element width
7875      * @param {Number} height The target element height
7876      */
7877     realign : function(l, t, w, h){
7878         if(!this.el){
7879             return;
7880         }
7881         var a = this.adjusts, d = this.el.dom, s = d.style;
7882         var iea = 0;
7883         s.left = (l+a.l)+"px";
7884         s.top = (t+a.t)+"px";
7885         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
7886  
7887         if(s.width != sws || s.height != shs){
7888             s.width = sws;
7889             s.height = shs;
7890             if(!Roo.isIE){
7891                 var cn = d.childNodes;
7892                 var sww = Math.max(0, (sw-12))+"px";
7893                 cn[0].childNodes[1].style.width = sww;
7894                 cn[1].childNodes[1].style.width = sww;
7895                 cn[2].childNodes[1].style.width = sww;
7896                 cn[1].style.height = Math.max(0, (sh-12))+"px";
7897             }
7898         }
7899     },
7900
7901     /**
7902      * Hides this shadow
7903      */
7904     hide : function(){
7905         if(this.el){
7906             this.el.dom.style.display = "none";
7907             Roo.Shadow.Pool.push(this.el);
7908             delete this.el;
7909         }
7910     },
7911
7912     /**
7913      * Adjust the z-index of this shadow
7914      * @param {Number} zindex The new z-index
7915      */
7916     setZIndex : function(z){
7917         this.zIndex = z;
7918         if(this.el){
7919             this.el.setStyle("z-index", z);
7920         }
7921     }
7922 };
7923
7924 // Private utility class that manages the internal Shadow cache
7925 Roo.Shadow.Pool = function(){
7926     var p = [];
7927     var markup = Roo.isIE ?
7928                  '<div class="x-ie-shadow"></div>' :
7929                  '<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>';
7930     return {
7931         pull : function(){
7932             var sh = p.shift();
7933             if(!sh){
7934                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
7935                 sh.autoBoxAdjust = false;
7936             }
7937             return sh;
7938         },
7939
7940         push : function(sh){
7941             p.push(sh);
7942         }
7943     };
7944 }();/*
7945  * Based on:
7946  * Ext JS Library 1.1.1
7947  * Copyright(c) 2006-2007, Ext JS, LLC.
7948  *
7949  * Originally Released Under LGPL - original licence link has changed is not relivant.
7950  *
7951  * Fork - LGPL
7952  * <script type="text/javascript">
7953  */
7954
7955
7956 /**
7957  * @class Roo.SplitBar
7958  * @extends Roo.util.Observable
7959  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
7960  * <br><br>
7961  * Usage:
7962  * <pre><code>
7963 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
7964                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
7965 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
7966 split.minSize = 100;
7967 split.maxSize = 600;
7968 split.animate = true;
7969 split.on('moved', splitterMoved);
7970 </code></pre>
7971  * @constructor
7972  * Create a new SplitBar
7973  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
7974  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
7975  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
7976  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
7977                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
7978                         position of the SplitBar).
7979  */
7980 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
7981     
7982     /** @private */
7983     this.el = Roo.get(dragElement, true);
7984     this.el.dom.unselectable = "on";
7985     /** @private */
7986     this.resizingEl = Roo.get(resizingElement, true);
7987
7988     /**
7989      * @private
7990      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
7991      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
7992      * @type Number
7993      */
7994     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
7995     
7996     /**
7997      * The minimum size of the resizing element. (Defaults to 0)
7998      * @type Number
7999      */
8000     this.minSize = 0;
8001     
8002     /**
8003      * The maximum size of the resizing element. (Defaults to 2000)
8004      * @type Number
8005      */
8006     this.maxSize = 2000;
8007     
8008     /**
8009      * Whether to animate the transition to the new size
8010      * @type Boolean
8011      */
8012     this.animate = false;
8013     
8014     /**
8015      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8016      * @type Boolean
8017      */
8018     this.useShim = false;
8019     
8020     /** @private */
8021     this.shim = null;
8022     
8023     if(!existingProxy){
8024         /** @private */
8025         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8026     }else{
8027         this.proxy = Roo.get(existingProxy).dom;
8028     }
8029     /** @private */
8030     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8031     
8032     /** @private */
8033     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8034     
8035     /** @private */
8036     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8037     
8038     /** @private */
8039     this.dragSpecs = {};
8040     
8041     /**
8042      * @private The adapter to use to positon and resize elements
8043      */
8044     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8045     this.adapter.init(this);
8046     
8047     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8048         /** @private */
8049         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8050         this.el.addClass("x-splitbar-h");
8051     }else{
8052         /** @private */
8053         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8054         this.el.addClass("x-splitbar-v");
8055     }
8056     
8057     this.addEvents({
8058         /**
8059          * @event resize
8060          * Fires when the splitter is moved (alias for {@link #event-moved})
8061          * @param {Roo.SplitBar} this
8062          * @param {Number} newSize the new width or height
8063          */
8064         "resize" : true,
8065         /**
8066          * @event moved
8067          * Fires when the splitter is moved
8068          * @param {Roo.SplitBar} this
8069          * @param {Number} newSize the new width or height
8070          */
8071         "moved" : true,
8072         /**
8073          * @event beforeresize
8074          * Fires before the splitter is dragged
8075          * @param {Roo.SplitBar} this
8076          */
8077         "beforeresize" : true,
8078
8079         "beforeapply" : true
8080     });
8081
8082     Roo.util.Observable.call(this);
8083 };
8084
8085 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8086     onStartProxyDrag : function(x, y){
8087         this.fireEvent("beforeresize", this);
8088         if(!this.overlay){
8089             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8090             o.unselectable();
8091             o.enableDisplayMode("block");
8092             // all splitbars share the same overlay
8093             Roo.SplitBar.prototype.overlay = o;
8094         }
8095         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8096         this.overlay.show();
8097         Roo.get(this.proxy).setDisplayed("block");
8098         var size = this.adapter.getElementSize(this);
8099         this.activeMinSize = this.getMinimumSize();;
8100         this.activeMaxSize = this.getMaximumSize();;
8101         var c1 = size - this.activeMinSize;
8102         var c2 = Math.max(this.activeMaxSize - size, 0);
8103         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8104             this.dd.resetConstraints();
8105             this.dd.setXConstraint(
8106                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8107                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8108             );
8109             this.dd.setYConstraint(0, 0);
8110         }else{
8111             this.dd.resetConstraints();
8112             this.dd.setXConstraint(0, 0);
8113             this.dd.setYConstraint(
8114                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8115                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8116             );
8117          }
8118         this.dragSpecs.startSize = size;
8119         this.dragSpecs.startPoint = [x, y];
8120         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8121     },
8122     
8123     /** 
8124      * @private Called after the drag operation by the DDProxy
8125      */
8126     onEndProxyDrag : function(e){
8127         Roo.get(this.proxy).setDisplayed(false);
8128         var endPoint = Roo.lib.Event.getXY(e);
8129         if(this.overlay){
8130             this.overlay.hide();
8131         }
8132         var newSize;
8133         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8134             newSize = this.dragSpecs.startSize + 
8135                 (this.placement == Roo.SplitBar.LEFT ?
8136                     endPoint[0] - this.dragSpecs.startPoint[0] :
8137                     this.dragSpecs.startPoint[0] - endPoint[0]
8138                 );
8139         }else{
8140             newSize = this.dragSpecs.startSize + 
8141                 (this.placement == Roo.SplitBar.TOP ?
8142                     endPoint[1] - this.dragSpecs.startPoint[1] :
8143                     this.dragSpecs.startPoint[1] - endPoint[1]
8144                 );
8145         }
8146         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8147         if(newSize != this.dragSpecs.startSize){
8148             if(this.fireEvent('beforeapply', this, newSize) !== false){
8149                 this.adapter.setElementSize(this, newSize);
8150                 this.fireEvent("moved", this, newSize);
8151                 this.fireEvent("resize", this, newSize);
8152             }
8153         }
8154     },
8155     
8156     /**
8157      * Get the adapter this SplitBar uses
8158      * @return The adapter object
8159      */
8160     getAdapter : function(){
8161         return this.adapter;
8162     },
8163     
8164     /**
8165      * Set the adapter this SplitBar uses
8166      * @param {Object} adapter A SplitBar adapter object
8167      */
8168     setAdapter : function(adapter){
8169         this.adapter = adapter;
8170         this.adapter.init(this);
8171     },
8172     
8173     /**
8174      * Gets the minimum size for the resizing element
8175      * @return {Number} The minimum size
8176      */
8177     getMinimumSize : function(){
8178         return this.minSize;
8179     },
8180     
8181     /**
8182      * Sets the minimum size for the resizing element
8183      * @param {Number} minSize The minimum size
8184      */
8185     setMinimumSize : function(minSize){
8186         this.minSize = minSize;
8187     },
8188     
8189     /**
8190      * Gets the maximum size for the resizing element
8191      * @return {Number} The maximum size
8192      */
8193     getMaximumSize : function(){
8194         return this.maxSize;
8195     },
8196     
8197     /**
8198      * Sets the maximum size for the resizing element
8199      * @param {Number} maxSize The maximum size
8200      */
8201     setMaximumSize : function(maxSize){
8202         this.maxSize = maxSize;
8203     },
8204     
8205     /**
8206      * Sets the initialize size for the resizing element
8207      * @param {Number} size The initial size
8208      */
8209     setCurrentSize : function(size){
8210         var oldAnimate = this.animate;
8211         this.animate = false;
8212         this.adapter.setElementSize(this, size);
8213         this.animate = oldAnimate;
8214     },
8215     
8216     /**
8217      * Destroy this splitbar. 
8218      * @param {Boolean} removeEl True to remove the element
8219      */
8220     destroy : function(removeEl){
8221         if(this.shim){
8222             this.shim.remove();
8223         }
8224         this.dd.unreg();
8225         this.proxy.parentNode.removeChild(this.proxy);
8226         if(removeEl){
8227             this.el.remove();
8228         }
8229     }
8230 });
8231
8232 /**
8233  * @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.
8234  */
8235 Roo.SplitBar.createProxy = function(dir){
8236     var proxy = new Roo.Element(document.createElement("div"));
8237     proxy.unselectable();
8238     var cls = 'x-splitbar-proxy';
8239     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8240     document.body.appendChild(proxy.dom);
8241     return proxy.dom;
8242 };
8243
8244 /** 
8245  * @class Roo.SplitBar.BasicLayoutAdapter
8246  * Default Adapter. It assumes the splitter and resizing element are not positioned
8247  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8248  */
8249 Roo.SplitBar.BasicLayoutAdapter = function(){
8250 };
8251
8252 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8253     // do nothing for now
8254     init : function(s){
8255     
8256     },
8257     /**
8258      * Called before drag operations to get the current size of the resizing element. 
8259      * @param {Roo.SplitBar} s The SplitBar using this adapter
8260      */
8261      getElementSize : function(s){
8262         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8263             return s.resizingEl.getWidth();
8264         }else{
8265             return s.resizingEl.getHeight();
8266         }
8267     },
8268     
8269     /**
8270      * Called after drag operations to set the size of the resizing element.
8271      * @param {Roo.SplitBar} s The SplitBar using this adapter
8272      * @param {Number} newSize The new size to set
8273      * @param {Function} onComplete A function to be invoked when resizing is complete
8274      */
8275     setElementSize : function(s, newSize, onComplete){
8276         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8277             if(!s.animate){
8278                 s.resizingEl.setWidth(newSize);
8279                 if(onComplete){
8280                     onComplete(s, newSize);
8281                 }
8282             }else{
8283                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8284             }
8285         }else{
8286             
8287             if(!s.animate){
8288                 s.resizingEl.setHeight(newSize);
8289                 if(onComplete){
8290                     onComplete(s, newSize);
8291                 }
8292             }else{
8293                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8294             }
8295         }
8296     }
8297 };
8298
8299 /** 
8300  *@class Roo.SplitBar.AbsoluteLayoutAdapter
8301  * @extends Roo.SplitBar.BasicLayoutAdapter
8302  * Adapter that  moves the splitter element to align with the resized sizing element. 
8303  * Used with an absolute positioned SplitBar.
8304  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
8305  * document.body, make sure you assign an id to the body element.
8306  */
8307 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
8308     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
8309     this.container = Roo.get(container);
8310 };
8311
8312 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
8313     init : function(s){
8314         this.basic.init(s);
8315     },
8316     
8317     getElementSize : function(s){
8318         return this.basic.getElementSize(s);
8319     },
8320     
8321     setElementSize : function(s, newSize, onComplete){
8322         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
8323     },
8324     
8325     moveSplitter : function(s){
8326         var yes = Roo.SplitBar;
8327         switch(s.placement){
8328             case yes.LEFT:
8329                 s.el.setX(s.resizingEl.getRight());
8330                 break;
8331             case yes.RIGHT:
8332                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
8333                 break;
8334             case yes.TOP:
8335                 s.el.setY(s.resizingEl.getBottom());
8336                 break;
8337             case yes.BOTTOM:
8338                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
8339                 break;
8340         }
8341     }
8342 };
8343
8344 /**
8345  * Orientation constant - Create a vertical SplitBar
8346  * @static
8347  * @type Number
8348  */
8349 Roo.SplitBar.VERTICAL = 1;
8350
8351 /**
8352  * Orientation constant - Create a horizontal SplitBar
8353  * @static
8354  * @type Number
8355  */
8356 Roo.SplitBar.HORIZONTAL = 2;
8357
8358 /**
8359  * Placement constant - The resizing element is to the left of the splitter element
8360  * @static
8361  * @type Number
8362  */
8363 Roo.SplitBar.LEFT = 1;
8364
8365 /**
8366  * Placement constant - The resizing element is to the right of the splitter element
8367  * @static
8368  * @type Number
8369  */
8370 Roo.SplitBar.RIGHT = 2;
8371
8372 /**
8373  * Placement constant - The resizing element is positioned above the splitter element
8374  * @static
8375  * @type Number
8376  */
8377 Roo.SplitBar.TOP = 3;
8378
8379 /**
8380  * Placement constant - The resizing element is positioned under splitter element
8381  * @static
8382  * @type Number
8383  */
8384 Roo.SplitBar.BOTTOM = 4;
8385 /*
8386  * Based on:
8387  * Ext JS Library 1.1.1
8388  * Copyright(c) 2006-2007, Ext JS, LLC.
8389  *
8390  * Originally Released Under LGPL - original licence link has changed is not relivant.
8391  *
8392  * Fork - LGPL
8393  * <script type="text/javascript">
8394  */
8395
8396 /**
8397  * @class Roo.View
8398  * @extends Roo.util.Observable
8399  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
8400  * This class also supports single and multi selection modes. <br>
8401  * Create a data model bound view:
8402  <pre><code>
8403  var store = new Roo.data.Store(...);
8404
8405  var view = new Roo.View({
8406     el : "my-element",
8407     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
8408  
8409     singleSelect: true,
8410     selectedClass: "ydataview-selected",
8411     store: store
8412  });
8413
8414  // listen for node click?
8415  view.on("click", function(vw, index, node, e){
8416  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
8417  });
8418
8419  // load XML data
8420  dataModel.load("foobar.xml");
8421  </code></pre>
8422  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
8423  * <br><br>
8424  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
8425  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
8426  * 
8427  * Note: old style constructor is still suported (container, template, config)
8428  * 
8429  * @constructor
8430  * Create a new View
8431  * @param {Object} config The config object
8432  * 
8433  */
8434 Roo.View = function(config, depreciated_tpl, depreciated_config){
8435     
8436     this.parent = false;
8437     
8438     if (typeof(depreciated_tpl) == 'undefined') {
8439         // new way.. - universal constructor.
8440         Roo.apply(this, config);
8441         this.el  = Roo.get(this.el);
8442     } else {
8443         // old format..
8444         this.el  = Roo.get(config);
8445         this.tpl = depreciated_tpl;
8446         Roo.apply(this, depreciated_config);
8447     }
8448     this.wrapEl  = this.el.wrap().wrap();
8449     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
8450     
8451     
8452     if(typeof(this.tpl) == "string"){
8453         this.tpl = new Roo.Template(this.tpl);
8454     } else {
8455         // support xtype ctors..
8456         this.tpl = new Roo.factory(this.tpl, Roo);
8457     }
8458     
8459     
8460     this.tpl.compile();
8461     
8462     /** @private */
8463     this.addEvents({
8464         /**
8465          * @event beforeclick
8466          * Fires before a click is processed. Returns false to cancel the default action.
8467          * @param {Roo.View} this
8468          * @param {Number} index The index of the target node
8469          * @param {HTMLElement} node The target node
8470          * @param {Roo.EventObject} e The raw event object
8471          */
8472             "beforeclick" : true,
8473         /**
8474          * @event click
8475          * Fires when a template node is clicked.
8476          * @param {Roo.View} this
8477          * @param {Number} index The index of the target node
8478          * @param {HTMLElement} node The target node
8479          * @param {Roo.EventObject} e The raw event object
8480          */
8481             "click" : true,
8482         /**
8483          * @event dblclick
8484          * Fires when a template node is double clicked.
8485          * @param {Roo.View} this
8486          * @param {Number} index The index of the target node
8487          * @param {HTMLElement} node The target node
8488          * @param {Roo.EventObject} e The raw event object
8489          */
8490             "dblclick" : true,
8491         /**
8492          * @event contextmenu
8493          * Fires when a template node is right clicked.
8494          * @param {Roo.View} this
8495          * @param {Number} index The index of the target node
8496          * @param {HTMLElement} node The target node
8497          * @param {Roo.EventObject} e The raw event object
8498          */
8499             "contextmenu" : true,
8500         /**
8501          * @event selectionchange
8502          * Fires when the selected nodes change.
8503          * @param {Roo.View} this
8504          * @param {Array} selections Array of the selected nodes
8505          */
8506             "selectionchange" : true,
8507     
8508         /**
8509          * @event beforeselect
8510          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
8511          * @param {Roo.View} this
8512          * @param {HTMLElement} node The node to be selected
8513          * @param {Array} selections Array of currently selected nodes
8514          */
8515             "beforeselect" : true,
8516         /**
8517          * @event preparedata
8518          * Fires on every row to render, to allow you to change the data.
8519          * @param {Roo.View} this
8520          * @param {Object} data to be rendered (change this)
8521          */
8522           "preparedata" : true
8523           
8524           
8525         });
8526
8527
8528
8529     this.el.on({
8530         "click": this.onClick,
8531         "dblclick": this.onDblClick,
8532         "contextmenu": this.onContextMenu,
8533         scope:this
8534     });
8535
8536     this.selections = [];
8537     this.nodes = [];
8538     this.cmp = new Roo.CompositeElementLite([]);
8539     if(this.store){
8540         this.store = Roo.factory(this.store, Roo.data);
8541         this.setStore(this.store, true);
8542     }
8543     
8544     if ( this.footer && this.footer.xtype) {
8545            
8546          var fctr = this.wrapEl.appendChild(document.createElement("div"));
8547         
8548         this.footer.dataSource = this.store;
8549         this.footer.container = fctr;
8550         this.footer = Roo.factory(this.footer, Roo);
8551         fctr.insertFirst(this.el);
8552         
8553         // this is a bit insane - as the paging toolbar seems to detach the el..
8554 //        dom.parentNode.parentNode.parentNode
8555          // they get detached?
8556     }
8557     
8558     
8559     Roo.View.superclass.constructor.call(this);
8560     
8561     
8562 };
8563
8564 Roo.extend(Roo.View, Roo.util.Observable, {
8565     
8566      /**
8567      * @cfg {Roo.data.Store} store Data store to load data from.
8568      */
8569     store : false,
8570     
8571     /**
8572      * @cfg {String|Roo.Element} el The container element.
8573      */
8574     el : '',
8575     
8576     /**
8577      * @cfg {String|Roo.Template} tpl The template used by this View 
8578      */
8579     tpl : false,
8580     /**
8581      * @cfg {String} dataName the named area of the template to use as the data area
8582      *                          Works with domtemplates roo-name="name"
8583      */
8584     dataName: false,
8585     /**
8586      * @cfg {String} selectedClass The css class to add to selected nodes
8587      */
8588     selectedClass : "x-view-selected",
8589      /**
8590      * @cfg {String} emptyText The empty text to show when nothing is loaded.
8591      */
8592     emptyText : "",
8593     
8594     /**
8595      * @cfg {String} text to display on mask (default Loading)
8596      */
8597     mask : false,
8598     /**
8599      * @cfg {Boolean} multiSelect Allow multiple selection
8600      */
8601     multiSelect : false,
8602     /**
8603      * @cfg {Boolean} singleSelect Allow single selection
8604      */
8605     singleSelect:  false,
8606     
8607     /**
8608      * @cfg {Boolean} toggleSelect - selecting 
8609      */
8610     toggleSelect : false,
8611     
8612     /**
8613      * @cfg {Boolean} tickable - selecting 
8614      */
8615     tickable : false,
8616     
8617     /**
8618      * Returns the element this view is bound to.
8619      * @return {Roo.Element}
8620      */
8621     getEl : function(){
8622         return this.wrapEl;
8623     },
8624     
8625     
8626
8627     /**
8628      * Refreshes the view. - called by datachanged on the store. - do not call directly.
8629      */
8630     refresh : function(){
8631         //Roo.log('refresh');
8632         var t = this.tpl;
8633         
8634         // if we are using something like 'domtemplate', then
8635         // the what gets used is:
8636         // t.applySubtemplate(NAME, data, wrapping data..)
8637         // the outer template then get' applied with
8638         //     the store 'extra data'
8639         // and the body get's added to the
8640         //      roo-name="data" node?
8641         //      <span class='roo-tpl-{name}'></span> ?????
8642         
8643         
8644         
8645         this.clearSelections();
8646         this.el.update("");
8647         var html = [];
8648         var records = this.store.getRange();
8649         if(records.length < 1) {
8650             
8651             // is this valid??  = should it render a template??
8652             
8653             this.el.update(this.emptyText);
8654             return;
8655         }
8656         var el = this.el;
8657         if (this.dataName) {
8658             this.el.update(t.apply(this.store.meta)); //????
8659             el = this.el.child('.roo-tpl-' + this.dataName);
8660         }
8661         
8662         for(var i = 0, len = records.length; i < len; i++){
8663             var data = this.prepareData(records[i].data, i, records[i]);
8664             this.fireEvent("preparedata", this, data, i, records[i]);
8665             
8666             var d = Roo.apply({}, data);
8667             
8668             if(this.tickable){
8669                 Roo.apply(d, {'roo-id' : Roo.id()});
8670                 
8671                 var _this = this;
8672             
8673                 Roo.each(this.parent.item, function(item){
8674                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
8675                         return;
8676                     }
8677                     Roo.apply(d, {'roo-data-checked' : 'checked'});
8678                 });
8679             }
8680             
8681             html[html.length] = Roo.util.Format.trim(
8682                 this.dataName ?
8683                     t.applySubtemplate(this.dataName, d, this.store.meta) :
8684                     t.apply(d)
8685             );
8686         }
8687         
8688         
8689         
8690         el.update(html.join(""));
8691         this.nodes = el.dom.childNodes;
8692         this.updateIndexes(0);
8693     },
8694     
8695
8696     /**
8697      * Function to override to reformat the data that is sent to
8698      * the template for each node.
8699      * DEPRICATED - use the preparedata event handler.
8700      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
8701      * a JSON object for an UpdateManager bound view).
8702      */
8703     prepareData : function(data, index, record)
8704     {
8705         this.fireEvent("preparedata", this, data, index, record);
8706         return data;
8707     },
8708
8709     onUpdate : function(ds, record){
8710         // Roo.log('on update');   
8711         this.clearSelections();
8712         var index = this.store.indexOf(record);
8713         var n = this.nodes[index];
8714         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
8715         n.parentNode.removeChild(n);
8716         this.updateIndexes(index, index);
8717     },
8718
8719     
8720     
8721 // --------- FIXME     
8722     onAdd : function(ds, records, index)
8723     {
8724         //Roo.log(['on Add', ds, records, index] );        
8725         this.clearSelections();
8726         if(this.nodes.length == 0){
8727             this.refresh();
8728             return;
8729         }
8730         var n = this.nodes[index];
8731         for(var i = 0, len = records.length; i < len; i++){
8732             var d = this.prepareData(records[i].data, i, records[i]);
8733             if(n){
8734                 this.tpl.insertBefore(n, d);
8735             }else{
8736                 
8737                 this.tpl.append(this.el, d);
8738             }
8739         }
8740         this.updateIndexes(index);
8741     },
8742
8743     onRemove : function(ds, record, index){
8744        // Roo.log('onRemove');
8745         this.clearSelections();
8746         var el = this.dataName  ?
8747             this.el.child('.roo-tpl-' + this.dataName) :
8748             this.el; 
8749         
8750         el.dom.removeChild(this.nodes[index]);
8751         this.updateIndexes(index);
8752     },
8753
8754     /**
8755      * Refresh an individual node.
8756      * @param {Number} index
8757      */
8758     refreshNode : function(index){
8759         this.onUpdate(this.store, this.store.getAt(index));
8760     },
8761
8762     updateIndexes : function(startIndex, endIndex){
8763         var ns = this.nodes;
8764         startIndex = startIndex || 0;
8765         endIndex = endIndex || ns.length - 1;
8766         for(var i = startIndex; i <= endIndex; i++){
8767             ns[i].nodeIndex = i;
8768         }
8769     },
8770
8771     /**
8772      * Changes the data store this view uses and refresh the view.
8773      * @param {Store} store
8774      */
8775     setStore : function(store, initial){
8776         if(!initial && this.store){
8777             this.store.un("datachanged", this.refresh);
8778             this.store.un("add", this.onAdd);
8779             this.store.un("remove", this.onRemove);
8780             this.store.un("update", this.onUpdate);
8781             this.store.un("clear", this.refresh);
8782             this.store.un("beforeload", this.onBeforeLoad);
8783             this.store.un("load", this.onLoad);
8784             this.store.un("loadexception", this.onLoad);
8785         }
8786         if(store){
8787           
8788             store.on("datachanged", this.refresh, this);
8789             store.on("add", this.onAdd, this);
8790             store.on("remove", this.onRemove, this);
8791             store.on("update", this.onUpdate, this);
8792             store.on("clear", this.refresh, this);
8793             store.on("beforeload", this.onBeforeLoad, this);
8794             store.on("load", this.onLoad, this);
8795             store.on("loadexception", this.onLoad, this);
8796         }
8797         
8798         if(store){
8799             this.refresh();
8800         }
8801     },
8802     /**
8803      * onbeforeLoad - masks the loading area.
8804      *
8805      */
8806     onBeforeLoad : function(store,opts)
8807     {
8808          //Roo.log('onBeforeLoad');   
8809         if (!opts.add) {
8810             this.el.update("");
8811         }
8812         this.el.mask(this.mask ? this.mask : "Loading" ); 
8813     },
8814     onLoad : function ()
8815     {
8816         this.el.unmask();
8817     },
8818     
8819
8820     /**
8821      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
8822      * @param {HTMLElement} node
8823      * @return {HTMLElement} The template node
8824      */
8825     findItemFromChild : function(node){
8826         var el = this.dataName  ?
8827             this.el.child('.roo-tpl-' + this.dataName,true) :
8828             this.el.dom; 
8829         
8830         if(!node || node.parentNode == el){
8831                     return node;
8832             }
8833             var p = node.parentNode;
8834             while(p && p != el){
8835             if(p.parentNode == el){
8836                 return p;
8837             }
8838             p = p.parentNode;
8839         }
8840             return null;
8841     },
8842
8843     /** @ignore */
8844     onClick : function(e){
8845         var item = this.findItemFromChild(e.getTarget());
8846         if(item){
8847             var index = this.indexOf(item);
8848             if(this.onItemClick(item, index, e) !== false){
8849                 this.fireEvent("click", this, index, item, e);
8850             }
8851         }else{
8852             this.clearSelections();
8853         }
8854     },
8855
8856     /** @ignore */
8857     onContextMenu : function(e){
8858         var item = this.findItemFromChild(e.getTarget());
8859         if(item){
8860             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
8861         }
8862     },
8863
8864     /** @ignore */
8865     onDblClick : function(e){
8866         var item = this.findItemFromChild(e.getTarget());
8867         if(item){
8868             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
8869         }
8870     },
8871
8872     onItemClick : function(item, index, e)
8873     {
8874         if(this.fireEvent("beforeclick", this, index, item, e) === false){
8875             return false;
8876         }
8877         if (this.toggleSelect) {
8878             var m = this.isSelected(item) ? 'unselect' : 'select';
8879             //Roo.log(m);
8880             var _t = this;
8881             _t[m](item, true, false);
8882             return true;
8883         }
8884         if(this.multiSelect || this.singleSelect){
8885             if(this.multiSelect && e.shiftKey && this.lastSelection){
8886                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
8887             }else{
8888                 this.select(item, this.multiSelect && e.ctrlKey);
8889                 this.lastSelection = item;
8890             }
8891             
8892             if(!this.tickable){
8893                 e.preventDefault();
8894             }
8895             
8896         }
8897         return true;
8898     },
8899
8900     /**
8901      * Get the number of selected nodes.
8902      * @return {Number}
8903      */
8904     getSelectionCount : function(){
8905         return this.selections.length;
8906     },
8907
8908     /**
8909      * Get the currently selected nodes.
8910      * @return {Array} An array of HTMLElements
8911      */
8912     getSelectedNodes : function(){
8913         return this.selections;
8914     },
8915
8916     /**
8917      * Get the indexes of the selected nodes.
8918      * @return {Array}
8919      */
8920     getSelectedIndexes : function(){
8921         var indexes = [], s = this.selections;
8922         for(var i = 0, len = s.length; i < len; i++){
8923             indexes.push(s[i].nodeIndex);
8924         }
8925         return indexes;
8926     },
8927
8928     /**
8929      * Clear all selections
8930      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
8931      */
8932     clearSelections : function(suppressEvent){
8933         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
8934             this.cmp.elements = this.selections;
8935             this.cmp.removeClass(this.selectedClass);
8936             this.selections = [];
8937             if(!suppressEvent){
8938                 this.fireEvent("selectionchange", this, this.selections);
8939             }
8940         }
8941     },
8942
8943     /**
8944      * Returns true if the passed node is selected
8945      * @param {HTMLElement/Number} node The node or node index
8946      * @return {Boolean}
8947      */
8948     isSelected : function(node){
8949         var s = this.selections;
8950         if(s.length < 1){
8951             return false;
8952         }
8953         node = this.getNode(node);
8954         return s.indexOf(node) !== -1;
8955     },
8956
8957     /**
8958      * Selects nodes.
8959      * @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
8960      * @param {Boolean} keepExisting (optional) true to keep existing selections
8961      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
8962      */
8963     select : function(nodeInfo, keepExisting, suppressEvent){
8964         if(nodeInfo instanceof Array){
8965             if(!keepExisting){
8966                 this.clearSelections(true);
8967             }
8968             for(var i = 0, len = nodeInfo.length; i < len; i++){
8969                 this.select(nodeInfo[i], true, true);
8970             }
8971             return;
8972         } 
8973         var node = this.getNode(nodeInfo);
8974         if(!node || this.isSelected(node)){
8975             return; // already selected.
8976         }
8977         if(!keepExisting){
8978             this.clearSelections(true);
8979         }
8980         
8981         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
8982             Roo.fly(node).addClass(this.selectedClass);
8983             this.selections.push(node);
8984             if(!suppressEvent){
8985                 this.fireEvent("selectionchange", this, this.selections);
8986             }
8987         }
8988         
8989         
8990     },
8991       /**
8992      * Unselects nodes.
8993      * @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
8994      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
8995      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
8996      */
8997     unselect : function(nodeInfo, keepExisting, suppressEvent)
8998     {
8999         if(nodeInfo instanceof Array){
9000             Roo.each(this.selections, function(s) {
9001                 this.unselect(s, nodeInfo);
9002             }, this);
9003             return;
9004         }
9005         var node = this.getNode(nodeInfo);
9006         if(!node || !this.isSelected(node)){
9007             //Roo.log("not selected");
9008             return; // not selected.
9009         }
9010         // fireevent???
9011         var ns = [];
9012         Roo.each(this.selections, function(s) {
9013             if (s == node ) {
9014                 Roo.fly(node).removeClass(this.selectedClass);
9015
9016                 return;
9017             }
9018             ns.push(s);
9019         },this);
9020         
9021         this.selections= ns;
9022         this.fireEvent("selectionchange", this, this.selections);
9023     },
9024
9025     /**
9026      * Gets a template node.
9027      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9028      * @return {HTMLElement} The node or null if it wasn't found
9029      */
9030     getNode : function(nodeInfo){
9031         if(typeof nodeInfo == "string"){
9032             return document.getElementById(nodeInfo);
9033         }else if(typeof nodeInfo == "number"){
9034             return this.nodes[nodeInfo];
9035         }
9036         return nodeInfo;
9037     },
9038
9039     /**
9040      * Gets a range template nodes.
9041      * @param {Number} startIndex
9042      * @param {Number} endIndex
9043      * @return {Array} An array of nodes
9044      */
9045     getNodes : function(start, end){
9046         var ns = this.nodes;
9047         start = start || 0;
9048         end = typeof end == "undefined" ? ns.length - 1 : end;
9049         var nodes = [];
9050         if(start <= end){
9051             for(var i = start; i <= end; i++){
9052                 nodes.push(ns[i]);
9053             }
9054         } else{
9055             for(var i = start; i >= end; i--){
9056                 nodes.push(ns[i]);
9057             }
9058         }
9059         return nodes;
9060     },
9061
9062     /**
9063      * Finds the index of the passed node
9064      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9065      * @return {Number} The index of the node or -1
9066      */
9067     indexOf : function(node){
9068         node = this.getNode(node);
9069         if(typeof node.nodeIndex == "number"){
9070             return node.nodeIndex;
9071         }
9072         var ns = this.nodes;
9073         for(var i = 0, len = ns.length; i < len; i++){
9074             if(ns[i] == node){
9075                 return i;
9076             }
9077         }
9078         return -1;
9079     }
9080 });
9081 /*
9082  * Based on:
9083  * Ext JS Library 1.1.1
9084  * Copyright(c) 2006-2007, Ext JS, LLC.
9085  *
9086  * Originally Released Under LGPL - original licence link has changed is not relivant.
9087  *
9088  * Fork - LGPL
9089  * <script type="text/javascript">
9090  */
9091
9092 /**
9093  * @class Roo.JsonView
9094  * @extends Roo.View
9095  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9096 <pre><code>
9097 var view = new Roo.JsonView({
9098     container: "my-element",
9099     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9100     multiSelect: true, 
9101     jsonRoot: "data" 
9102 });
9103
9104 // listen for node click?
9105 view.on("click", function(vw, index, node, e){
9106     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9107 });
9108
9109 // direct load of JSON data
9110 view.load("foobar.php");
9111
9112 // Example from my blog list
9113 var tpl = new Roo.Template(
9114     '&lt;div class="entry"&gt;' +
9115     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9116     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9117     "&lt;/div&gt;&lt;hr /&gt;"
9118 );
9119
9120 var moreView = new Roo.JsonView({
9121     container :  "entry-list", 
9122     template : tpl,
9123     jsonRoot: "posts"
9124 });
9125 moreView.on("beforerender", this.sortEntries, this);
9126 moreView.load({
9127     url: "/blog/get-posts.php",
9128     params: "allposts=true",
9129     text: "Loading Blog Entries..."
9130 });
9131 </code></pre>
9132
9133 * Note: old code is supported with arguments : (container, template, config)
9134
9135
9136  * @constructor
9137  * Create a new JsonView
9138  * 
9139  * @param {Object} config The config object
9140  * 
9141  */
9142 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9143     
9144     
9145     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9146
9147     var um = this.el.getUpdateManager();
9148     um.setRenderer(this);
9149     um.on("update", this.onLoad, this);
9150     um.on("failure", this.onLoadException, this);
9151
9152     /**
9153      * @event beforerender
9154      * Fires before rendering of the downloaded JSON data.
9155      * @param {Roo.JsonView} this
9156      * @param {Object} data The JSON data loaded
9157      */
9158     /**
9159      * @event load
9160      * Fires when data is loaded.
9161      * @param {Roo.JsonView} this
9162      * @param {Object} data The JSON data loaded
9163      * @param {Object} response The raw Connect response object
9164      */
9165     /**
9166      * @event loadexception
9167      * Fires when loading fails.
9168      * @param {Roo.JsonView} this
9169      * @param {Object} response The raw Connect response object
9170      */
9171     this.addEvents({
9172         'beforerender' : true,
9173         'load' : true,
9174         'loadexception' : true
9175     });
9176 };
9177 Roo.extend(Roo.JsonView, Roo.View, {
9178     /**
9179      * @type {String} The root property in the loaded JSON object that contains the data
9180      */
9181     jsonRoot : "",
9182
9183     /**
9184      * Refreshes the view.
9185      */
9186     refresh : function(){
9187         this.clearSelections();
9188         this.el.update("");
9189         var html = [];
9190         var o = this.jsonData;
9191         if(o && o.length > 0){
9192             for(var i = 0, len = o.length; i < len; i++){
9193                 var data = this.prepareData(o[i], i, o);
9194                 html[html.length] = this.tpl.apply(data);
9195             }
9196         }else{
9197             html.push(this.emptyText);
9198         }
9199         this.el.update(html.join(""));
9200         this.nodes = this.el.dom.childNodes;
9201         this.updateIndexes(0);
9202     },
9203
9204     /**
9205      * 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.
9206      * @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:
9207      <pre><code>
9208      view.load({
9209          url: "your-url.php",
9210          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9211          callback: yourFunction,
9212          scope: yourObject, //(optional scope)
9213          discardUrl: false,
9214          nocache: false,
9215          text: "Loading...",
9216          timeout: 30,
9217          scripts: false
9218      });
9219      </code></pre>
9220      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9221      * 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.
9222      * @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}
9223      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9224      * @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.
9225      */
9226     load : function(){
9227         var um = this.el.getUpdateManager();
9228         um.update.apply(um, arguments);
9229     },
9230
9231     render : function(el, response){
9232         this.clearSelections();
9233         this.el.update("");
9234         var o;
9235         try{
9236             o = Roo.util.JSON.decode(response.responseText);
9237             if(this.jsonRoot){
9238                 
9239                 o = o[this.jsonRoot];
9240             }
9241         } catch(e){
9242         }
9243         /**
9244          * The current JSON data or null
9245          */
9246         this.jsonData = o;
9247         this.beforeRender();
9248         this.refresh();
9249     },
9250
9251 /**
9252  * Get the number of records in the current JSON dataset
9253  * @return {Number}
9254  */
9255     getCount : function(){
9256         return this.jsonData ? this.jsonData.length : 0;
9257     },
9258
9259 /**
9260  * Returns the JSON object for the specified node(s)
9261  * @param {HTMLElement/Array} node The node or an array of nodes
9262  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9263  * you get the JSON object for the node
9264  */
9265     getNodeData : function(node){
9266         if(node instanceof Array){
9267             var data = [];
9268             for(var i = 0, len = node.length; i < len; i++){
9269                 data.push(this.getNodeData(node[i]));
9270             }
9271             return data;
9272         }
9273         return this.jsonData[this.indexOf(node)] || null;
9274     },
9275
9276     beforeRender : function(){
9277         this.snapshot = this.jsonData;
9278         if(this.sortInfo){
9279             this.sort.apply(this, this.sortInfo);
9280         }
9281         this.fireEvent("beforerender", this, this.jsonData);
9282     },
9283
9284     onLoad : function(el, o){
9285         this.fireEvent("load", this, this.jsonData, o);
9286     },
9287
9288     onLoadException : function(el, o){
9289         this.fireEvent("loadexception", this, o);
9290     },
9291
9292 /**
9293  * Filter the data by a specific property.
9294  * @param {String} property A property on your JSON objects
9295  * @param {String/RegExp} value Either string that the property values
9296  * should start with, or a RegExp to test against the property
9297  */
9298     filter : function(property, value){
9299         if(this.jsonData){
9300             var data = [];
9301             var ss = this.snapshot;
9302             if(typeof value == "string"){
9303                 var vlen = value.length;
9304                 if(vlen == 0){
9305                     this.clearFilter();
9306                     return;
9307                 }
9308                 value = value.toLowerCase();
9309                 for(var i = 0, len = ss.length; i < len; i++){
9310                     var o = ss[i];
9311                     if(o[property].substr(0, vlen).toLowerCase() == value){
9312                         data.push(o);
9313                     }
9314                 }
9315             } else if(value.exec){ // regex?
9316                 for(var i = 0, len = ss.length; i < len; i++){
9317                     var o = ss[i];
9318                     if(value.test(o[property])){
9319                         data.push(o);
9320                     }
9321                 }
9322             } else{
9323                 return;
9324             }
9325             this.jsonData = data;
9326             this.refresh();
9327         }
9328     },
9329
9330 /**
9331  * Filter by a function. The passed function will be called with each
9332  * object in the current dataset. If the function returns true the value is kept,
9333  * otherwise it is filtered.
9334  * @param {Function} fn
9335  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9336  */
9337     filterBy : function(fn, scope){
9338         if(this.jsonData){
9339             var data = [];
9340             var ss = this.snapshot;
9341             for(var i = 0, len = ss.length; i < len; i++){
9342                 var o = ss[i];
9343                 if(fn.call(scope || this, o)){
9344                     data.push(o);
9345                 }
9346             }
9347             this.jsonData = data;
9348             this.refresh();
9349         }
9350     },
9351
9352 /**
9353  * Clears the current filter.
9354  */
9355     clearFilter : function(){
9356         if(this.snapshot && this.jsonData != this.snapshot){
9357             this.jsonData = this.snapshot;
9358             this.refresh();
9359         }
9360     },
9361
9362
9363 /**
9364  * Sorts the data for this view and refreshes it.
9365  * @param {String} property A property on your JSON objects to sort on
9366  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9367  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9368  */
9369     sort : function(property, dir, sortType){
9370         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9371         if(this.jsonData){
9372             var p = property;
9373             var dsc = dir && dir.toLowerCase() == "desc";
9374             var f = function(o1, o2){
9375                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9376                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9377                 ;
9378                 if(v1 < v2){
9379                     return dsc ? +1 : -1;
9380                 } else if(v1 > v2){
9381                     return dsc ? -1 : +1;
9382                 } else{
9383                     return 0;
9384                 }
9385             };
9386             this.jsonData.sort(f);
9387             this.refresh();
9388             if(this.jsonData != this.snapshot){
9389                 this.snapshot.sort(f);
9390             }
9391         }
9392     }
9393 });/*
9394  * Based on:
9395  * Ext JS Library 1.1.1
9396  * Copyright(c) 2006-2007, Ext JS, LLC.
9397  *
9398  * Originally Released Under LGPL - original licence link has changed is not relivant.
9399  *
9400  * Fork - LGPL
9401  * <script type="text/javascript">
9402  */
9403  
9404
9405 /**
9406  * @class Roo.ColorPalette
9407  * @extends Roo.Component
9408  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9409  * Here's an example of typical usage:
9410  * <pre><code>
9411 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9412 cp.render('my-div');
9413
9414 cp.on('select', function(palette, selColor){
9415     // do something with selColor
9416 });
9417 </code></pre>
9418  * @constructor
9419  * Create a new ColorPalette
9420  * @param {Object} config The config object
9421  */
9422 Roo.ColorPalette = function(config){
9423     Roo.ColorPalette.superclass.constructor.call(this, config);
9424     this.addEvents({
9425         /**
9426              * @event select
9427              * Fires when a color is selected
9428              * @param {ColorPalette} this
9429              * @param {String} color The 6-digit color hex code (without the # symbol)
9430              */
9431         select: true
9432     });
9433
9434     if(this.handler){
9435         this.on("select", this.handler, this.scope, true);
9436     }
9437 };
9438 Roo.extend(Roo.ColorPalette, Roo.Component, {
9439     /**
9440      * @cfg {String} itemCls
9441      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9442      */
9443     itemCls : "x-color-palette",
9444     /**
9445      * @cfg {String} value
9446      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9447      * the hex codes are case-sensitive.
9448      */
9449     value : null,
9450     clickEvent:'click',
9451     // private
9452     ctype: "Roo.ColorPalette",
9453
9454     /**
9455      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9456      */
9457     allowReselect : false,
9458
9459     /**
9460      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9461      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9462      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9463      * of colors with the width setting until the box is symmetrical.</p>
9464      * <p>You can override individual colors if needed:</p>
9465      * <pre><code>
9466 var cp = new Roo.ColorPalette();
9467 cp.colors[0] = "FF0000";  // change the first box to red
9468 </code></pre>
9469
9470 Or you can provide a custom array of your own for complete control:
9471 <pre><code>
9472 var cp = new Roo.ColorPalette();
9473 cp.colors = ["000000", "993300", "333300"];
9474 </code></pre>
9475      * @type Array
9476      */
9477     colors : [
9478         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9479         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9480         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9481         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9482         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9483     ],
9484
9485     // private
9486     onRender : function(container, position){
9487         var t = new Roo.MasterTemplate(
9488             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
9489         );
9490         var c = this.colors;
9491         for(var i = 0, len = c.length; i < len; i++){
9492             t.add([c[i]]);
9493         }
9494         var el = document.createElement("div");
9495         el.className = this.itemCls;
9496         t.overwrite(el);
9497         container.dom.insertBefore(el, position);
9498         this.el = Roo.get(el);
9499         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
9500         if(this.clickEvent != 'click'){
9501             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
9502         }
9503     },
9504
9505     // private
9506     afterRender : function(){
9507         Roo.ColorPalette.superclass.afterRender.call(this);
9508         if(this.value){
9509             var s = this.value;
9510             this.value = null;
9511             this.select(s);
9512         }
9513     },
9514
9515     // private
9516     handleClick : function(e, t){
9517         e.preventDefault();
9518         if(!this.disabled){
9519             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
9520             this.select(c.toUpperCase());
9521         }
9522     },
9523
9524     /**
9525      * Selects the specified color in the palette (fires the select event)
9526      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
9527      */
9528     select : function(color){
9529         color = color.replace("#", "");
9530         if(color != this.value || this.allowReselect){
9531             var el = this.el;
9532             if(this.value){
9533                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
9534             }
9535             el.child("a.color-"+color).addClass("x-color-palette-sel");
9536             this.value = color;
9537             this.fireEvent("select", this, color);
9538         }
9539     }
9540 });/*
9541  * Based on:
9542  * Ext JS Library 1.1.1
9543  * Copyright(c) 2006-2007, Ext JS, LLC.
9544  *
9545  * Originally Released Under LGPL - original licence link has changed is not relivant.
9546  *
9547  * Fork - LGPL
9548  * <script type="text/javascript">
9549  */
9550  
9551 /**
9552  * @class Roo.DatePicker
9553  * @extends Roo.Component
9554  * Simple date picker class.
9555  * @constructor
9556  * Create a new DatePicker
9557  * @param {Object} config The config object
9558  */
9559 Roo.DatePicker = function(config){
9560     Roo.DatePicker.superclass.constructor.call(this, config);
9561
9562     this.value = config && config.value ?
9563                  config.value.clearTime() : new Date().clearTime();
9564
9565     this.addEvents({
9566         /**
9567              * @event select
9568              * Fires when a date is selected
9569              * @param {DatePicker} this
9570              * @param {Date} date The selected date
9571              */
9572         'select': true,
9573         /**
9574              * @event monthchange
9575              * Fires when the displayed month changes 
9576              * @param {DatePicker} this
9577              * @param {Date} date The selected month
9578              */
9579         'monthchange': true
9580     });
9581
9582     if(this.handler){
9583         this.on("select", this.handler,  this.scope || this);
9584     }
9585     // build the disabledDatesRE
9586     if(!this.disabledDatesRE && this.disabledDates){
9587         var dd = this.disabledDates;
9588         var re = "(?:";
9589         for(var i = 0; i < dd.length; i++){
9590             re += dd[i];
9591             if(i != dd.length-1) {
9592                 re += "|";
9593             }
9594         }
9595         this.disabledDatesRE = new RegExp(re + ")");
9596     }
9597 };
9598
9599 Roo.extend(Roo.DatePicker, Roo.Component, {
9600     /**
9601      * @cfg {String} todayText
9602      * The text to display on the button that selects the current date (defaults to "Today")
9603      */
9604     todayText : "Today",
9605     /**
9606      * @cfg {String} okText
9607      * The text to display on the ok button
9608      */
9609     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
9610     /**
9611      * @cfg {String} cancelText
9612      * The text to display on the cancel button
9613      */
9614     cancelText : "Cancel",
9615     /**
9616      * @cfg {String} todayTip
9617      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
9618      */
9619     todayTip : "{0} (Spacebar)",
9620     /**
9621      * @cfg {Date} minDate
9622      * Minimum allowable date (JavaScript date object, defaults to null)
9623      */
9624     minDate : null,
9625     /**
9626      * @cfg {Date} maxDate
9627      * Maximum allowable date (JavaScript date object, defaults to null)
9628      */
9629     maxDate : null,
9630     /**
9631      * @cfg {String} minText
9632      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
9633      */
9634     minText : "This date is before the minimum date",
9635     /**
9636      * @cfg {String} maxText
9637      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
9638      */
9639     maxText : "This date is after the maximum date",
9640     /**
9641      * @cfg {String} format
9642      * The default date format string which can be overriden for localization support.  The format must be
9643      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
9644      */
9645     format : "m/d/y",
9646     /**
9647      * @cfg {Array} disabledDays
9648      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
9649      */
9650     disabledDays : null,
9651     /**
9652      * @cfg {String} disabledDaysText
9653      * The tooltip to display when the date falls on a disabled day (defaults to "")
9654      */
9655     disabledDaysText : "",
9656     /**
9657      * @cfg {RegExp} disabledDatesRE
9658      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
9659      */
9660     disabledDatesRE : null,
9661     /**
9662      * @cfg {String} disabledDatesText
9663      * The tooltip text to display when the date falls on a disabled date (defaults to "")
9664      */
9665     disabledDatesText : "",
9666     /**
9667      * @cfg {Boolean} constrainToViewport
9668      * True to constrain the date picker to the viewport (defaults to true)
9669      */
9670     constrainToViewport : true,
9671     /**
9672      * @cfg {Array} monthNames
9673      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
9674      */
9675     monthNames : Date.monthNames,
9676     /**
9677      * @cfg {Array} dayNames
9678      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
9679      */
9680     dayNames : Date.dayNames,
9681     /**
9682      * @cfg {String} nextText
9683      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
9684      */
9685     nextText: 'Next Month (Control+Right)',
9686     /**
9687      * @cfg {String} prevText
9688      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
9689      */
9690     prevText: 'Previous Month (Control+Left)',
9691     /**
9692      * @cfg {String} monthYearText
9693      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
9694      */
9695     monthYearText: 'Choose a month (Control+Up/Down to move years)',
9696     /**
9697      * @cfg {Number} startDay
9698      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9699      */
9700     startDay : 0,
9701     /**
9702      * @cfg {Bool} showClear
9703      * Show a clear button (usefull for date form elements that can be blank.)
9704      */
9705     
9706     showClear: false,
9707     
9708     /**
9709      * Sets the value of the date field
9710      * @param {Date} value The date to set
9711      */
9712     setValue : function(value){
9713         var old = this.value;
9714         
9715         if (typeof(value) == 'string') {
9716          
9717             value = Date.parseDate(value, this.format);
9718         }
9719         if (!value) {
9720             value = new Date();
9721         }
9722         
9723         this.value = value.clearTime(true);
9724         if(this.el){
9725             this.update(this.value);
9726         }
9727     },
9728
9729     /**
9730      * Gets the current selected value of the date field
9731      * @return {Date} The selected date
9732      */
9733     getValue : function(){
9734         return this.value;
9735     },
9736
9737     // private
9738     focus : function(){
9739         if(this.el){
9740             this.update(this.activeDate);
9741         }
9742     },
9743
9744     // privateval
9745     onRender : function(container, position){
9746         
9747         var m = [
9748              '<table cellspacing="0">',
9749                 '<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>',
9750                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
9751         var dn = this.dayNames;
9752         for(var i = 0; i < 7; i++){
9753             var d = this.startDay+i;
9754             if(d > 6){
9755                 d = d-7;
9756             }
9757             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
9758         }
9759         m[m.length] = "</tr></thead><tbody><tr>";
9760         for(var i = 0; i < 42; i++) {
9761             if(i % 7 == 0 && i != 0){
9762                 m[m.length] = "</tr><tr>";
9763             }
9764             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
9765         }
9766         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
9767             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
9768
9769         var el = document.createElement("div");
9770         el.className = "x-date-picker";
9771         el.innerHTML = m.join("");
9772
9773         container.dom.insertBefore(el, position);
9774
9775         this.el = Roo.get(el);
9776         this.eventEl = Roo.get(el.firstChild);
9777
9778         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
9779             handler: this.showPrevMonth,
9780             scope: this,
9781             preventDefault:true,
9782             stopDefault:true
9783         });
9784
9785         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
9786             handler: this.showNextMonth,
9787             scope: this,
9788             preventDefault:true,
9789             stopDefault:true
9790         });
9791
9792         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
9793
9794         this.monthPicker = this.el.down('div.x-date-mp');
9795         this.monthPicker.enableDisplayMode('block');
9796         
9797         var kn = new Roo.KeyNav(this.eventEl, {
9798             "left" : function(e){
9799                 e.ctrlKey ?
9800                     this.showPrevMonth() :
9801                     this.update(this.activeDate.add("d", -1));
9802             },
9803
9804             "right" : function(e){
9805                 e.ctrlKey ?
9806                     this.showNextMonth() :
9807                     this.update(this.activeDate.add("d", 1));
9808             },
9809
9810             "up" : function(e){
9811                 e.ctrlKey ?
9812                     this.showNextYear() :
9813                     this.update(this.activeDate.add("d", -7));
9814             },
9815
9816             "down" : function(e){
9817                 e.ctrlKey ?
9818                     this.showPrevYear() :
9819                     this.update(this.activeDate.add("d", 7));
9820             },
9821
9822             "pageUp" : function(e){
9823                 this.showNextMonth();
9824             },
9825
9826             "pageDown" : function(e){
9827                 this.showPrevMonth();
9828             },
9829
9830             "enter" : function(e){
9831                 e.stopPropagation();
9832                 return true;
9833             },
9834
9835             scope : this
9836         });
9837
9838         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
9839
9840         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
9841
9842         this.el.unselectable();
9843         
9844         this.cells = this.el.select("table.x-date-inner tbody td");
9845         this.textNodes = this.el.query("table.x-date-inner tbody span");
9846
9847         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
9848             text: "&#160;",
9849             tooltip: this.monthYearText
9850         });
9851
9852         this.mbtn.on('click', this.showMonthPicker, this);
9853         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
9854
9855
9856         var today = (new Date()).dateFormat(this.format);
9857         
9858         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
9859         if (this.showClear) {
9860             baseTb.add( new Roo.Toolbar.Fill());
9861         }
9862         baseTb.add({
9863             text: String.format(this.todayText, today),
9864             tooltip: String.format(this.todayTip, today),
9865             handler: this.selectToday,
9866             scope: this
9867         });
9868         
9869         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
9870             
9871         //});
9872         if (this.showClear) {
9873             
9874             baseTb.add( new Roo.Toolbar.Fill());
9875             baseTb.add({
9876                 text: '&#160;',
9877                 cls: 'x-btn-icon x-btn-clear',
9878                 handler: function() {
9879                     //this.value = '';
9880                     this.fireEvent("select", this, '');
9881                 },
9882                 scope: this
9883             });
9884         }
9885         
9886         
9887         if(Roo.isIE){
9888             this.el.repaint();
9889         }
9890         this.update(this.value);
9891     },
9892
9893     createMonthPicker : function(){
9894         if(!this.monthPicker.dom.firstChild){
9895             var buf = ['<table border="0" cellspacing="0">'];
9896             for(var i = 0; i < 6; i++){
9897                 buf.push(
9898                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
9899                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
9900                     i == 0 ?
9901                     '<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>' :
9902                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
9903                 );
9904             }
9905             buf.push(
9906                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
9907                     this.okText,
9908                     '</button><button type="button" class="x-date-mp-cancel">',
9909                     this.cancelText,
9910                     '</button></td></tr>',
9911                 '</table>'
9912             );
9913             this.monthPicker.update(buf.join(''));
9914             this.monthPicker.on('click', this.onMonthClick, this);
9915             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
9916
9917             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
9918             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
9919
9920             this.mpMonths.each(function(m, a, i){
9921                 i += 1;
9922                 if((i%2) == 0){
9923                     m.dom.xmonth = 5 + Math.round(i * .5);
9924                 }else{
9925                     m.dom.xmonth = Math.round((i-1) * .5);
9926                 }
9927             });
9928         }
9929     },
9930
9931     showMonthPicker : function(){
9932         this.createMonthPicker();
9933         var size = this.el.getSize();
9934         this.monthPicker.setSize(size);
9935         this.monthPicker.child('table').setSize(size);
9936
9937         this.mpSelMonth = (this.activeDate || this.value).getMonth();
9938         this.updateMPMonth(this.mpSelMonth);
9939         this.mpSelYear = (this.activeDate || this.value).getFullYear();
9940         this.updateMPYear(this.mpSelYear);
9941
9942         this.monthPicker.slideIn('t', {duration:.2});
9943     },
9944
9945     updateMPYear : function(y){
9946         this.mpyear = y;
9947         var ys = this.mpYears.elements;
9948         for(var i = 1; i <= 10; i++){
9949             var td = ys[i-1], y2;
9950             if((i%2) == 0){
9951                 y2 = y + Math.round(i * .5);
9952                 td.firstChild.innerHTML = y2;
9953                 td.xyear = y2;
9954             }else{
9955                 y2 = y - (5-Math.round(i * .5));
9956                 td.firstChild.innerHTML = y2;
9957                 td.xyear = y2;
9958             }
9959             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
9960         }
9961     },
9962
9963     updateMPMonth : function(sm){
9964         this.mpMonths.each(function(m, a, i){
9965             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
9966         });
9967     },
9968
9969     selectMPMonth: function(m){
9970         
9971     },
9972
9973     onMonthClick : function(e, t){
9974         e.stopEvent();
9975         var el = new Roo.Element(t), pn;
9976         if(el.is('button.x-date-mp-cancel')){
9977             this.hideMonthPicker();
9978         }
9979         else if(el.is('button.x-date-mp-ok')){
9980             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
9981             this.hideMonthPicker();
9982         }
9983         else if(pn = el.up('td.x-date-mp-month', 2)){
9984             this.mpMonths.removeClass('x-date-mp-sel');
9985             pn.addClass('x-date-mp-sel');
9986             this.mpSelMonth = pn.dom.xmonth;
9987         }
9988         else if(pn = el.up('td.x-date-mp-year', 2)){
9989             this.mpYears.removeClass('x-date-mp-sel');
9990             pn.addClass('x-date-mp-sel');
9991             this.mpSelYear = pn.dom.xyear;
9992         }
9993         else if(el.is('a.x-date-mp-prev')){
9994             this.updateMPYear(this.mpyear-10);
9995         }
9996         else if(el.is('a.x-date-mp-next')){
9997             this.updateMPYear(this.mpyear+10);
9998         }
9999     },
10000
10001     onMonthDblClick : function(e, t){
10002         e.stopEvent();
10003         var el = new Roo.Element(t), pn;
10004         if(pn = el.up('td.x-date-mp-month', 2)){
10005             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10006             this.hideMonthPicker();
10007         }
10008         else if(pn = el.up('td.x-date-mp-year', 2)){
10009             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10010             this.hideMonthPicker();
10011         }
10012     },
10013
10014     hideMonthPicker : function(disableAnim){
10015         if(this.monthPicker){
10016             if(disableAnim === true){
10017                 this.monthPicker.hide();
10018             }else{
10019                 this.monthPicker.slideOut('t', {duration:.2});
10020             }
10021         }
10022     },
10023
10024     // private
10025     showPrevMonth : function(e){
10026         this.update(this.activeDate.add("mo", -1));
10027     },
10028
10029     // private
10030     showNextMonth : function(e){
10031         this.update(this.activeDate.add("mo", 1));
10032     },
10033
10034     // private
10035     showPrevYear : function(){
10036         this.update(this.activeDate.add("y", -1));
10037     },
10038
10039     // private
10040     showNextYear : function(){
10041         this.update(this.activeDate.add("y", 1));
10042     },
10043
10044     // private
10045     handleMouseWheel : function(e){
10046         var delta = e.getWheelDelta();
10047         if(delta > 0){
10048             this.showPrevMonth();
10049             e.stopEvent();
10050         } else if(delta < 0){
10051             this.showNextMonth();
10052             e.stopEvent();
10053         }
10054     },
10055
10056     // private
10057     handleDateClick : function(e, t){
10058         e.stopEvent();
10059         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10060             this.setValue(new Date(t.dateValue));
10061             this.fireEvent("select", this, this.value);
10062         }
10063     },
10064
10065     // private
10066     selectToday : function(){
10067         this.setValue(new Date().clearTime());
10068         this.fireEvent("select", this, this.value);
10069     },
10070
10071     // private
10072     update : function(date)
10073     {
10074         var vd = this.activeDate;
10075         this.activeDate = date;
10076         if(vd && this.el){
10077             var t = date.getTime();
10078             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10079                 this.cells.removeClass("x-date-selected");
10080                 this.cells.each(function(c){
10081                    if(c.dom.firstChild.dateValue == t){
10082                        c.addClass("x-date-selected");
10083                        setTimeout(function(){
10084                             try{c.dom.firstChild.focus();}catch(e){}
10085                        }, 50);
10086                        return false;
10087                    }
10088                 });
10089                 return;
10090             }
10091         }
10092         
10093         var days = date.getDaysInMonth();
10094         var firstOfMonth = date.getFirstDateOfMonth();
10095         var startingPos = firstOfMonth.getDay()-this.startDay;
10096
10097         if(startingPos <= this.startDay){
10098             startingPos += 7;
10099         }
10100
10101         var pm = date.add("mo", -1);
10102         var prevStart = pm.getDaysInMonth()-startingPos;
10103
10104         var cells = this.cells.elements;
10105         var textEls = this.textNodes;
10106         days += startingPos;
10107
10108         // convert everything to numbers so it's fast
10109         var day = 86400000;
10110         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10111         var today = new Date().clearTime().getTime();
10112         var sel = date.clearTime().getTime();
10113         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10114         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10115         var ddMatch = this.disabledDatesRE;
10116         var ddText = this.disabledDatesText;
10117         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10118         var ddaysText = this.disabledDaysText;
10119         var format = this.format;
10120
10121         var setCellClass = function(cal, cell){
10122             cell.title = "";
10123             var t = d.getTime();
10124             cell.firstChild.dateValue = t;
10125             if(t == today){
10126                 cell.className += " x-date-today";
10127                 cell.title = cal.todayText;
10128             }
10129             if(t == sel){
10130                 cell.className += " x-date-selected";
10131                 setTimeout(function(){
10132                     try{cell.firstChild.focus();}catch(e){}
10133                 }, 50);
10134             }
10135             // disabling
10136             if(t < min) {
10137                 cell.className = " x-date-disabled";
10138                 cell.title = cal.minText;
10139                 return;
10140             }
10141             if(t > max) {
10142                 cell.className = " x-date-disabled";
10143                 cell.title = cal.maxText;
10144                 return;
10145             }
10146             if(ddays){
10147                 if(ddays.indexOf(d.getDay()) != -1){
10148                     cell.title = ddaysText;
10149                     cell.className = " x-date-disabled";
10150                 }
10151             }
10152             if(ddMatch && format){
10153                 var fvalue = d.dateFormat(format);
10154                 if(ddMatch.test(fvalue)){
10155                     cell.title = ddText.replace("%0", fvalue);
10156                     cell.className = " x-date-disabled";
10157                 }
10158             }
10159         };
10160
10161         var i = 0;
10162         for(; i < startingPos; i++) {
10163             textEls[i].innerHTML = (++prevStart);
10164             d.setDate(d.getDate()+1);
10165             cells[i].className = "x-date-prevday";
10166             setCellClass(this, cells[i]);
10167         }
10168         for(; i < days; i++){
10169             intDay = i - startingPos + 1;
10170             textEls[i].innerHTML = (intDay);
10171             d.setDate(d.getDate()+1);
10172             cells[i].className = "x-date-active";
10173             setCellClass(this, cells[i]);
10174         }
10175         var extraDays = 0;
10176         for(; i < 42; i++) {
10177              textEls[i].innerHTML = (++extraDays);
10178              d.setDate(d.getDate()+1);
10179              cells[i].className = "x-date-nextday";
10180              setCellClass(this, cells[i]);
10181         }
10182
10183         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10184         this.fireEvent('monthchange', this, date);
10185         
10186         if(!this.internalRender){
10187             var main = this.el.dom.firstChild;
10188             var w = main.offsetWidth;
10189             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10190             Roo.fly(main).setWidth(w);
10191             this.internalRender = true;
10192             // opera does not respect the auto grow header center column
10193             // then, after it gets a width opera refuses to recalculate
10194             // without a second pass
10195             if(Roo.isOpera && !this.secondPass){
10196                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10197                 this.secondPass = true;
10198                 this.update.defer(10, this, [date]);
10199             }
10200         }
10201         
10202         
10203     }
10204 });        /*
10205  * Based on:
10206  * Ext JS Library 1.1.1
10207  * Copyright(c) 2006-2007, Ext JS, LLC.
10208  *
10209  * Originally Released Under LGPL - original licence link has changed is not relivant.
10210  *
10211  * Fork - LGPL
10212  * <script type="text/javascript">
10213  */
10214 /**
10215  * @class Roo.TabPanel
10216  * @extends Roo.util.Observable
10217  * A lightweight tab container.
10218  * <br><br>
10219  * Usage:
10220  * <pre><code>
10221 // basic tabs 1, built from existing content
10222 var tabs = new Roo.TabPanel("tabs1");
10223 tabs.addTab("script", "View Script");
10224 tabs.addTab("markup", "View Markup");
10225 tabs.activate("script");
10226
10227 // more advanced tabs, built from javascript
10228 var jtabs = new Roo.TabPanel("jtabs");
10229 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10230
10231 // set up the UpdateManager
10232 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10233 var updater = tab2.getUpdateManager();
10234 updater.setDefaultUrl("ajax1.htm");
10235 tab2.on('activate', updater.refresh, updater, true);
10236
10237 // Use setUrl for Ajax loading
10238 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10239 tab3.setUrl("ajax2.htm", null, true);
10240
10241 // Disabled tab
10242 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10243 tab4.disable();
10244
10245 jtabs.activate("jtabs-1");
10246  * </code></pre>
10247  * @constructor
10248  * Create a new TabPanel.
10249  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10250  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10251  */
10252 Roo.TabPanel = function(container, config){
10253     /**
10254     * The container element for this TabPanel.
10255     * @type Roo.Element
10256     */
10257     this.el = Roo.get(container, true);
10258     if(config){
10259         if(typeof config == "boolean"){
10260             this.tabPosition = config ? "bottom" : "top";
10261         }else{
10262             Roo.apply(this, config);
10263         }
10264     }
10265     if(this.tabPosition == "bottom"){
10266         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10267         this.el.addClass("x-tabs-bottom");
10268     }
10269     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10270     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10271     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10272     if(Roo.isIE){
10273         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10274     }
10275     if(this.tabPosition != "bottom"){
10276         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10277          * @type Roo.Element
10278          */
10279         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10280         this.el.addClass("x-tabs-top");
10281     }
10282     this.items = [];
10283
10284     this.bodyEl.setStyle("position", "relative");
10285
10286     this.active = null;
10287     this.activateDelegate = this.activate.createDelegate(this);
10288
10289     this.addEvents({
10290         /**
10291          * @event tabchange
10292          * Fires when the active tab changes
10293          * @param {Roo.TabPanel} this
10294          * @param {Roo.TabPanelItem} activePanel The new active tab
10295          */
10296         "tabchange": true,
10297         /**
10298          * @event beforetabchange
10299          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10300          * @param {Roo.TabPanel} this
10301          * @param {Object} e Set cancel to true on this object to cancel the tab change
10302          * @param {Roo.TabPanelItem} tab The tab being changed to
10303          */
10304         "beforetabchange" : true
10305     });
10306
10307     Roo.EventManager.onWindowResize(this.onResize, this);
10308     this.cpad = this.el.getPadding("lr");
10309     this.hiddenCount = 0;
10310
10311
10312     // toolbar on the tabbar support...
10313     if (this.toolbar) {
10314         var tcfg = this.toolbar;
10315         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
10316         this.toolbar = new Roo.Toolbar(tcfg);
10317         if (Roo.isSafari) {
10318             var tbl = tcfg.container.child('table', true);
10319             tbl.setAttribute('width', '100%');
10320         }
10321         
10322     }
10323    
10324
10325
10326     Roo.TabPanel.superclass.constructor.call(this);
10327 };
10328
10329 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10330     /*
10331      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10332      */
10333     tabPosition : "top",
10334     /*
10335      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10336      */
10337     currentTabWidth : 0,
10338     /*
10339      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10340      */
10341     minTabWidth : 40,
10342     /*
10343      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10344      */
10345     maxTabWidth : 250,
10346     /*
10347      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10348      */
10349     preferredTabWidth : 175,
10350     /*
10351      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10352      */
10353     resizeTabs : false,
10354     /*
10355      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10356      */
10357     monitorResize : true,
10358     /*
10359      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
10360      */
10361     toolbar : false,
10362
10363     /**
10364      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10365      * @param {String} id The id of the div to use <b>or create</b>
10366      * @param {String} text The text for the tab
10367      * @param {String} content (optional) Content to put in the TabPanelItem body
10368      * @param {Boolean} closable (optional) True to create a close icon on the tab
10369      * @return {Roo.TabPanelItem} The created TabPanelItem
10370      */
10371     addTab : function(id, text, content, closable){
10372         var item = new Roo.TabPanelItem(this, id, text, closable);
10373         this.addTabItem(item);
10374         if(content){
10375             item.setContent(content);
10376         }
10377         return item;
10378     },
10379
10380     /**
10381      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10382      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10383      * @return {Roo.TabPanelItem}
10384      */
10385     getTab : function(id){
10386         return this.items[id];
10387     },
10388
10389     /**
10390      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10391      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10392      */
10393     hideTab : function(id){
10394         var t = this.items[id];
10395         if(!t.isHidden()){
10396            t.setHidden(true);
10397            this.hiddenCount++;
10398            this.autoSizeTabs();
10399         }
10400     },
10401
10402     /**
10403      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10404      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10405      */
10406     unhideTab : function(id){
10407         var t = this.items[id];
10408         if(t.isHidden()){
10409            t.setHidden(false);
10410            this.hiddenCount--;
10411            this.autoSizeTabs();
10412         }
10413     },
10414
10415     /**
10416      * Adds an existing {@link Roo.TabPanelItem}.
10417      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10418      */
10419     addTabItem : function(item){
10420         this.items[item.id] = item;
10421         this.items.push(item);
10422         if(this.resizeTabs){
10423            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10424            this.autoSizeTabs();
10425         }else{
10426             item.autoSize();
10427         }
10428     },
10429
10430     /**
10431      * Removes a {@link Roo.TabPanelItem}.
10432      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10433      */
10434     removeTab : function(id){
10435         var items = this.items;
10436         var tab = items[id];
10437         if(!tab) { return; }
10438         var index = items.indexOf(tab);
10439         if(this.active == tab && items.length > 1){
10440             var newTab = this.getNextAvailable(index);
10441             if(newTab) {
10442                 newTab.activate();
10443             }
10444         }
10445         this.stripEl.dom.removeChild(tab.pnode.dom);
10446         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10447             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10448         }
10449         items.splice(index, 1);
10450         delete this.items[tab.id];
10451         tab.fireEvent("close", tab);
10452         tab.purgeListeners();
10453         this.autoSizeTabs();
10454     },
10455
10456     getNextAvailable : function(start){
10457         var items = this.items;
10458         var index = start;
10459         // look for a next tab that will slide over to
10460         // replace the one being removed
10461         while(index < items.length){
10462             var item = items[++index];
10463             if(item && !item.isHidden()){
10464                 return item;
10465             }
10466         }
10467         // if one isn't found select the previous tab (on the left)
10468         index = start;
10469         while(index >= 0){
10470             var item = items[--index];
10471             if(item && !item.isHidden()){
10472                 return item;
10473             }
10474         }
10475         return null;
10476     },
10477
10478     /**
10479      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10480      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10481      */
10482     disableTab : function(id){
10483         var tab = this.items[id];
10484         if(tab && this.active != tab){
10485             tab.disable();
10486         }
10487     },
10488
10489     /**
10490      * Enables a {@link Roo.TabPanelItem} that is disabled.
10491      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10492      */
10493     enableTab : function(id){
10494         var tab = this.items[id];
10495         tab.enable();
10496     },
10497
10498     /**
10499      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10500      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10501      * @return {Roo.TabPanelItem} The TabPanelItem.
10502      */
10503     activate : function(id){
10504         var tab = this.items[id];
10505         if(!tab){
10506             return null;
10507         }
10508         if(tab == this.active || tab.disabled){
10509             return tab;
10510         }
10511         var e = {};
10512         this.fireEvent("beforetabchange", this, e, tab);
10513         if(e.cancel !== true && !tab.disabled){
10514             if(this.active){
10515                 this.active.hide();
10516             }
10517             this.active = this.items[id];
10518             this.active.show();
10519             this.fireEvent("tabchange", this, this.active);
10520         }
10521         return tab;
10522     },
10523
10524     /**
10525      * Gets the active {@link Roo.TabPanelItem}.
10526      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
10527      */
10528     getActiveTab : function(){
10529         return this.active;
10530     },
10531
10532     /**
10533      * Updates the tab body element to fit the height of the container element
10534      * for overflow scrolling
10535      * @param {Number} targetHeight (optional) Override the starting height from the elements height
10536      */
10537     syncHeight : function(targetHeight){
10538         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
10539         var bm = this.bodyEl.getMargins();
10540         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
10541         this.bodyEl.setHeight(newHeight);
10542         return newHeight;
10543     },
10544
10545     onResize : function(){
10546         if(this.monitorResize){
10547             this.autoSizeTabs();
10548         }
10549     },
10550
10551     /**
10552      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
10553      */
10554     beginUpdate : function(){
10555         this.updating = true;
10556     },
10557
10558     /**
10559      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
10560      */
10561     endUpdate : function(){
10562         this.updating = false;
10563         this.autoSizeTabs();
10564     },
10565
10566     /**
10567      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
10568      */
10569     autoSizeTabs : function(){
10570         var count = this.items.length;
10571         var vcount = count - this.hiddenCount;
10572         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
10573             return;
10574         }
10575         var w = Math.max(this.el.getWidth() - this.cpad, 10);
10576         var availWidth = Math.floor(w / vcount);
10577         var b = this.stripBody;
10578         if(b.getWidth() > w){
10579             var tabs = this.items;
10580             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
10581             if(availWidth < this.minTabWidth){
10582                 /*if(!this.sleft){    // incomplete scrolling code
10583                     this.createScrollButtons();
10584                 }
10585                 this.showScroll();
10586                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
10587             }
10588         }else{
10589             if(this.currentTabWidth < this.preferredTabWidth){
10590                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
10591             }
10592         }
10593     },
10594
10595     /**
10596      * Returns the number of tabs in this TabPanel.
10597      * @return {Number}
10598      */
10599      getCount : function(){
10600          return this.items.length;
10601      },
10602
10603     /**
10604      * Resizes all the tabs to the passed width
10605      * @param {Number} The new width
10606      */
10607     setTabWidth : function(width){
10608         this.currentTabWidth = width;
10609         for(var i = 0, len = this.items.length; i < len; i++) {
10610                 if(!this.items[i].isHidden()) {
10611                 this.items[i].setWidth(width);
10612             }
10613         }
10614     },
10615
10616     /**
10617      * Destroys this TabPanel
10618      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
10619      */
10620     destroy : function(removeEl){
10621         Roo.EventManager.removeResizeListener(this.onResize, this);
10622         for(var i = 0, len = this.items.length; i < len; i++){
10623             this.items[i].purgeListeners();
10624         }
10625         if(removeEl === true){
10626             this.el.update("");
10627             this.el.remove();
10628         }
10629     }
10630 });
10631
10632 /**
10633  * @class Roo.TabPanelItem
10634  * @extends Roo.util.Observable
10635  * Represents an individual item (tab plus body) in a TabPanel.
10636  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
10637  * @param {String} id The id of this TabPanelItem
10638  * @param {String} text The text for the tab of this TabPanelItem
10639  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
10640  */
10641 Roo.TabPanelItem = function(tabPanel, id, text, closable){
10642     /**
10643      * The {@link Roo.TabPanel} this TabPanelItem belongs to
10644      * @type Roo.TabPanel
10645      */
10646     this.tabPanel = tabPanel;
10647     /**
10648      * The id for this TabPanelItem
10649      * @type String
10650      */
10651     this.id = id;
10652     /** @private */
10653     this.disabled = false;
10654     /** @private */
10655     this.text = text;
10656     /** @private */
10657     this.loaded = false;
10658     this.closable = closable;
10659
10660     /**
10661      * The body element for this TabPanelItem.
10662      * @type Roo.Element
10663      */
10664     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
10665     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
10666     this.bodyEl.setStyle("display", "block");
10667     this.bodyEl.setStyle("zoom", "1");
10668     this.hideAction();
10669
10670     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
10671     /** @private */
10672     this.el = Roo.get(els.el, true);
10673     this.inner = Roo.get(els.inner, true);
10674     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
10675     this.pnode = Roo.get(els.el.parentNode, true);
10676     this.el.on("mousedown", this.onTabMouseDown, this);
10677     this.el.on("click", this.onTabClick, this);
10678     /** @private */
10679     if(closable){
10680         var c = Roo.get(els.close, true);
10681         c.dom.title = this.closeText;
10682         c.addClassOnOver("close-over");
10683         c.on("click", this.closeClick, this);
10684      }
10685
10686     this.addEvents({
10687          /**
10688          * @event activate
10689          * Fires when this tab becomes the active tab.
10690          * @param {Roo.TabPanel} tabPanel The parent TabPanel
10691          * @param {Roo.TabPanelItem} this
10692          */
10693         "activate": true,
10694         /**
10695          * @event beforeclose
10696          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
10697          * @param {Roo.TabPanelItem} this
10698          * @param {Object} e Set cancel to true on this object to cancel the close.
10699          */
10700         "beforeclose": true,
10701         /**
10702          * @event close
10703          * Fires when this tab is closed.
10704          * @param {Roo.TabPanelItem} this
10705          */
10706          "close": true,
10707         /**
10708          * @event deactivate
10709          * Fires when this tab is no longer the active tab.
10710          * @param {Roo.TabPanel} tabPanel The parent TabPanel
10711          * @param {Roo.TabPanelItem} this
10712          */
10713          "deactivate" : true
10714     });
10715     this.hidden = false;
10716
10717     Roo.TabPanelItem.superclass.constructor.call(this);
10718 };
10719
10720 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
10721     purgeListeners : function(){
10722        Roo.util.Observable.prototype.purgeListeners.call(this);
10723        this.el.removeAllListeners();
10724     },
10725     /**
10726      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
10727      */
10728     show : function(){
10729         this.pnode.addClass("on");
10730         this.showAction();
10731         if(Roo.isOpera){
10732             this.tabPanel.stripWrap.repaint();
10733         }
10734         this.fireEvent("activate", this.tabPanel, this);
10735     },
10736
10737     /**
10738      * Returns true if this tab is the active tab.
10739      * @return {Boolean}
10740      */
10741     isActive : function(){
10742         return this.tabPanel.getActiveTab() == this;
10743     },
10744
10745     /**
10746      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
10747      */
10748     hide : function(){
10749         this.pnode.removeClass("on");
10750         this.hideAction();
10751         this.fireEvent("deactivate", this.tabPanel, this);
10752     },
10753
10754     hideAction : function(){
10755         this.bodyEl.hide();
10756         this.bodyEl.setStyle("position", "absolute");
10757         this.bodyEl.setLeft("-20000px");
10758         this.bodyEl.setTop("-20000px");
10759     },
10760
10761     showAction : function(){
10762         this.bodyEl.setStyle("position", "relative");
10763         this.bodyEl.setTop("");
10764         this.bodyEl.setLeft("");
10765         this.bodyEl.show();
10766     },
10767
10768     /**
10769      * Set the tooltip for the tab.
10770      * @param {String} tooltip The tab's tooltip
10771      */
10772     setTooltip : function(text){
10773         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
10774             this.textEl.dom.qtip = text;
10775             this.textEl.dom.removeAttribute('title');
10776         }else{
10777             this.textEl.dom.title = text;
10778         }
10779     },
10780
10781     onTabClick : function(e){
10782         e.preventDefault();
10783         this.tabPanel.activate(this.id);
10784     },
10785
10786     onTabMouseDown : function(e){
10787         e.preventDefault();
10788         this.tabPanel.activate(this.id);
10789     },
10790
10791     getWidth : function(){
10792         return this.inner.getWidth();
10793     },
10794
10795     setWidth : function(width){
10796         var iwidth = width - this.pnode.getPadding("lr");
10797         this.inner.setWidth(iwidth);
10798         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
10799         this.pnode.setWidth(width);
10800     },
10801
10802     /**
10803      * Show or hide the tab
10804      * @param {Boolean} hidden True to hide or false to show.
10805      */
10806     setHidden : function(hidden){
10807         this.hidden = hidden;
10808         this.pnode.setStyle("display", hidden ? "none" : "");
10809     },
10810
10811     /**
10812      * Returns true if this tab is "hidden"
10813      * @return {Boolean}
10814      */
10815     isHidden : function(){
10816         return this.hidden;
10817     },
10818
10819     /**
10820      * Returns the text for this tab
10821      * @return {String}
10822      */
10823     getText : function(){
10824         return this.text;
10825     },
10826
10827     autoSize : function(){
10828         //this.el.beginMeasure();
10829         this.textEl.setWidth(1);
10830         /*
10831          *  #2804 [new] Tabs in Roojs
10832          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
10833          */
10834         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
10835         //this.el.endMeasure();
10836     },
10837
10838     /**
10839      * Sets the text for the tab (Note: this also sets the tooltip text)
10840      * @param {String} text The tab's text and tooltip
10841      */
10842     setText : function(text){
10843         this.text = text;
10844         this.textEl.update(text);
10845         this.setTooltip(text);
10846         if(!this.tabPanel.resizeTabs){
10847             this.autoSize();
10848         }
10849     },
10850     /**
10851      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
10852      */
10853     activate : function(){
10854         this.tabPanel.activate(this.id);
10855     },
10856
10857     /**
10858      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
10859      */
10860     disable : function(){
10861         if(this.tabPanel.active != this){
10862             this.disabled = true;
10863             this.pnode.addClass("disabled");
10864         }
10865     },
10866
10867     /**
10868      * Enables this TabPanelItem if it was previously disabled.
10869      */
10870     enable : function(){
10871         this.disabled = false;
10872         this.pnode.removeClass("disabled");
10873     },
10874
10875     /**
10876      * Sets the content for this TabPanelItem.
10877      * @param {String} content The content
10878      * @param {Boolean} loadScripts true to look for and load scripts
10879      */
10880     setContent : function(content, loadScripts){
10881         this.bodyEl.update(content, loadScripts);
10882     },
10883
10884     /**
10885      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
10886      * @return {Roo.UpdateManager} The UpdateManager
10887      */
10888     getUpdateManager : function(){
10889         return this.bodyEl.getUpdateManager();
10890     },
10891
10892     /**
10893      * Set a URL to be used to load the content for this TabPanelItem.
10894      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
10895      * @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)
10896      * @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)
10897      * @return {Roo.UpdateManager} The UpdateManager
10898      */
10899     setUrl : function(url, params, loadOnce){
10900         if(this.refreshDelegate){
10901             this.un('activate', this.refreshDelegate);
10902         }
10903         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
10904         this.on("activate", this.refreshDelegate);
10905         return this.bodyEl.getUpdateManager();
10906     },
10907
10908     /** @private */
10909     _handleRefresh : function(url, params, loadOnce){
10910         if(!loadOnce || !this.loaded){
10911             var updater = this.bodyEl.getUpdateManager();
10912             updater.update(url, params, this._setLoaded.createDelegate(this));
10913         }
10914     },
10915
10916     /**
10917      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
10918      *   Will fail silently if the setUrl method has not been called.
10919      *   This does not activate the panel, just updates its content.
10920      */
10921     refresh : function(){
10922         if(this.refreshDelegate){
10923            this.loaded = false;
10924            this.refreshDelegate();
10925         }
10926     },
10927
10928     /** @private */
10929     _setLoaded : function(){
10930         this.loaded = true;
10931     },
10932
10933     /** @private */
10934     closeClick : function(e){
10935         var o = {};
10936         e.stopEvent();
10937         this.fireEvent("beforeclose", this, o);
10938         if(o.cancel !== true){
10939             this.tabPanel.removeTab(this.id);
10940         }
10941     },
10942     /**
10943      * The text displayed in the tooltip for the close icon.
10944      * @type String
10945      */
10946     closeText : "Close this tab"
10947 });
10948
10949 /** @private */
10950 Roo.TabPanel.prototype.createStrip = function(container){
10951     var strip = document.createElement("div");
10952     strip.className = "x-tabs-wrap";
10953     container.appendChild(strip);
10954     return strip;
10955 };
10956 /** @private */
10957 Roo.TabPanel.prototype.createStripList = function(strip){
10958     // div wrapper for retard IE
10959     // returns the "tr" element.
10960     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
10961         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
10962         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
10963     return strip.firstChild.firstChild.firstChild.firstChild;
10964 };
10965 /** @private */
10966 Roo.TabPanel.prototype.createBody = function(container){
10967     var body = document.createElement("div");
10968     Roo.id(body, "tab-body");
10969     Roo.fly(body).addClass("x-tabs-body");
10970     container.appendChild(body);
10971     return body;
10972 };
10973 /** @private */
10974 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
10975     var body = Roo.getDom(id);
10976     if(!body){
10977         body = document.createElement("div");
10978         body.id = id;
10979     }
10980     Roo.fly(body).addClass("x-tabs-item-body");
10981     bodyEl.insertBefore(body, bodyEl.firstChild);
10982     return body;
10983 };
10984 /** @private */
10985 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
10986     var td = document.createElement("td");
10987     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
10988     //stripEl.appendChild(td);
10989     if(closable){
10990         td.className = "x-tabs-closable";
10991         if(!this.closeTpl){
10992             this.closeTpl = new Roo.Template(
10993                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
10994                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
10995                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
10996             );
10997         }
10998         var el = this.closeTpl.overwrite(td, {"text": text});
10999         var close = el.getElementsByTagName("div")[0];
11000         var inner = el.getElementsByTagName("em")[0];
11001         return {"el": el, "close": close, "inner": inner};
11002     } else {
11003         if(!this.tabTpl){
11004             this.tabTpl = new Roo.Template(
11005                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11006                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11007             );
11008         }
11009         var el = this.tabTpl.overwrite(td, {"text": text});
11010         var inner = el.getElementsByTagName("em")[0];
11011         return {"el": el, "inner": inner};
11012     }
11013 };/*
11014  * Based on:
11015  * Ext JS Library 1.1.1
11016  * Copyright(c) 2006-2007, Ext JS, LLC.
11017  *
11018  * Originally Released Under LGPL - original licence link has changed is not relivant.
11019  *
11020  * Fork - LGPL
11021  * <script type="text/javascript">
11022  */
11023
11024 /**
11025  * @class Roo.Button
11026  * @extends Roo.util.Observable
11027  * Simple Button class
11028  * @cfg {String} text The button text
11029  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11030  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11031  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11032  * @cfg {Object} scope The scope of the handler
11033  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11034  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11035  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11036  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11037  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11038  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11039    applies if enableToggle = true)
11040  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11041  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11042   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11043  * @constructor
11044  * Create a new button
11045  * @param {Object} config The config object
11046  */
11047 Roo.Button = function(renderTo, config)
11048 {
11049     if (!config) {
11050         config = renderTo;
11051         renderTo = config.renderTo || false;
11052     }
11053     
11054     Roo.apply(this, config);
11055     this.addEvents({
11056         /**
11057              * @event click
11058              * Fires when this button is clicked
11059              * @param {Button} this
11060              * @param {EventObject} e The click event
11061              */
11062             "click" : true,
11063         /**
11064              * @event toggle
11065              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11066              * @param {Button} this
11067              * @param {Boolean} pressed
11068              */
11069             "toggle" : true,
11070         /**
11071              * @event mouseover
11072              * Fires when the mouse hovers over the button
11073              * @param {Button} this
11074              * @param {Event} e The event object
11075              */
11076         'mouseover' : true,
11077         /**
11078              * @event mouseout
11079              * Fires when the mouse exits the button
11080              * @param {Button} this
11081              * @param {Event} e The event object
11082              */
11083         'mouseout': true,
11084          /**
11085              * @event render
11086              * Fires when the button is rendered
11087              * @param {Button} this
11088              */
11089         'render': true
11090     });
11091     if(this.menu){
11092         this.menu = Roo.menu.MenuMgr.get(this.menu);
11093     }
11094     // register listeners first!!  - so render can be captured..
11095     Roo.util.Observable.call(this);
11096     if(renderTo){
11097         this.render(renderTo);
11098     }
11099     
11100   
11101 };
11102
11103 Roo.extend(Roo.Button, Roo.util.Observable, {
11104     /**
11105      * 
11106      */
11107     
11108     /**
11109      * Read-only. True if this button is hidden
11110      * @type Boolean
11111      */
11112     hidden : false,
11113     /**
11114      * Read-only. True if this button is disabled
11115      * @type Boolean
11116      */
11117     disabled : false,
11118     /**
11119      * Read-only. True if this button is pressed (only if enableToggle = true)
11120      * @type Boolean
11121      */
11122     pressed : false,
11123
11124     /**
11125      * @cfg {Number} tabIndex 
11126      * The DOM tabIndex for this button (defaults to undefined)
11127      */
11128     tabIndex : undefined,
11129
11130     /**
11131      * @cfg {Boolean} enableToggle
11132      * True to enable pressed/not pressed toggling (defaults to false)
11133      */
11134     enableToggle: false,
11135     /**
11136      * @cfg {Mixed} menu
11137      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11138      */
11139     menu : undefined,
11140     /**
11141      * @cfg {String} menuAlign
11142      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11143      */
11144     menuAlign : "tl-bl?",
11145
11146     /**
11147      * @cfg {String} iconCls
11148      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11149      */
11150     iconCls : undefined,
11151     /**
11152      * @cfg {String} type
11153      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11154      */
11155     type : 'button',
11156
11157     // private
11158     menuClassTarget: 'tr',
11159
11160     /**
11161      * @cfg {String} clickEvent
11162      * The type of event to map to the button's event handler (defaults to 'click')
11163      */
11164     clickEvent : 'click',
11165
11166     /**
11167      * @cfg {Boolean} handleMouseEvents
11168      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11169      */
11170     handleMouseEvents : true,
11171
11172     /**
11173      * @cfg {String} tooltipType
11174      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11175      */
11176     tooltipType : 'qtip',
11177
11178     /**
11179      * @cfg {String} cls
11180      * A CSS class to apply to the button's main element.
11181      */
11182     
11183     /**
11184      * @cfg {Roo.Template} template (Optional)
11185      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11186      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11187      * require code modifications if required elements (e.g. a button) aren't present.
11188      */
11189
11190     // private
11191     render : function(renderTo){
11192         var btn;
11193         if(this.hideParent){
11194             this.parentEl = Roo.get(renderTo);
11195         }
11196         if(!this.dhconfig){
11197             if(!this.template){
11198                 if(!Roo.Button.buttonTemplate){
11199                     // hideous table template
11200                     Roo.Button.buttonTemplate = new Roo.Template(
11201                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11202                         '<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>',
11203                         "</tr></tbody></table>");
11204                 }
11205                 this.template = Roo.Button.buttonTemplate;
11206             }
11207             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11208             var btnEl = btn.child("button:first");
11209             btnEl.on('focus', this.onFocus, this);
11210             btnEl.on('blur', this.onBlur, this);
11211             if(this.cls){
11212                 btn.addClass(this.cls);
11213             }
11214             if(this.icon){
11215                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11216             }
11217             if(this.iconCls){
11218                 btnEl.addClass(this.iconCls);
11219                 if(!this.cls){
11220                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11221                 }
11222             }
11223             if(this.tabIndex !== undefined){
11224                 btnEl.dom.tabIndex = this.tabIndex;
11225             }
11226             if(this.tooltip){
11227                 if(typeof this.tooltip == 'object'){
11228                     Roo.QuickTips.tips(Roo.apply({
11229                           target: btnEl.id
11230                     }, this.tooltip));
11231                 } else {
11232                     btnEl.dom[this.tooltipType] = this.tooltip;
11233                 }
11234             }
11235         }else{
11236             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11237         }
11238         this.el = btn;
11239         if(this.id){
11240             this.el.dom.id = this.el.id = this.id;
11241         }
11242         if(this.menu){
11243             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11244             this.menu.on("show", this.onMenuShow, this);
11245             this.menu.on("hide", this.onMenuHide, this);
11246         }
11247         btn.addClass("x-btn");
11248         if(Roo.isIE && !Roo.isIE7){
11249             this.autoWidth.defer(1, this);
11250         }else{
11251             this.autoWidth();
11252         }
11253         if(this.handleMouseEvents){
11254             btn.on("mouseover", this.onMouseOver, this);
11255             btn.on("mouseout", this.onMouseOut, this);
11256             btn.on("mousedown", this.onMouseDown, this);
11257         }
11258         btn.on(this.clickEvent, this.onClick, this);
11259         //btn.on("mouseup", this.onMouseUp, this);
11260         if(this.hidden){
11261             this.hide();
11262         }
11263         if(this.disabled){
11264             this.disable();
11265         }
11266         Roo.ButtonToggleMgr.register(this);
11267         if(this.pressed){
11268             this.el.addClass("x-btn-pressed");
11269         }
11270         if(this.repeat){
11271             var repeater = new Roo.util.ClickRepeater(btn,
11272                 typeof this.repeat == "object" ? this.repeat : {}
11273             );
11274             repeater.on("click", this.onClick,  this);
11275         }
11276         
11277         this.fireEvent('render', this);
11278         
11279     },
11280     /**
11281      * Returns the button's underlying element
11282      * @return {Roo.Element} The element
11283      */
11284     getEl : function(){
11285         return this.el;  
11286     },
11287     
11288     /**
11289      * Destroys this Button and removes any listeners.
11290      */
11291     destroy : function(){
11292         Roo.ButtonToggleMgr.unregister(this);
11293         this.el.removeAllListeners();
11294         this.purgeListeners();
11295         this.el.remove();
11296     },
11297
11298     // private
11299     autoWidth : function(){
11300         if(this.el){
11301             this.el.setWidth("auto");
11302             if(Roo.isIE7 && Roo.isStrict){
11303                 var ib = this.el.child('button');
11304                 if(ib && ib.getWidth() > 20){
11305                     ib.clip();
11306                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11307                 }
11308             }
11309             if(this.minWidth){
11310                 if(this.hidden){
11311                     this.el.beginMeasure();
11312                 }
11313                 if(this.el.getWidth() < this.minWidth){
11314                     this.el.setWidth(this.minWidth);
11315                 }
11316                 if(this.hidden){
11317                     this.el.endMeasure();
11318                 }
11319             }
11320         }
11321     },
11322
11323     /**
11324      * Assigns this button's click handler
11325      * @param {Function} handler The function to call when the button is clicked
11326      * @param {Object} scope (optional) Scope for the function passed in
11327      */
11328     setHandler : function(handler, scope){
11329         this.handler = handler;
11330         this.scope = scope;  
11331     },
11332     
11333     /**
11334      * Sets this button's text
11335      * @param {String} text The button text
11336      */
11337     setText : function(text){
11338         this.text = text;
11339         if(this.el){
11340             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11341         }
11342         this.autoWidth();
11343     },
11344     
11345     /**
11346      * Gets the text for this button
11347      * @return {String} The button text
11348      */
11349     getText : function(){
11350         return this.text;  
11351     },
11352     
11353     /**
11354      * Show this button
11355      */
11356     show: function(){
11357         this.hidden = false;
11358         if(this.el){
11359             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11360         }
11361     },
11362     
11363     /**
11364      * Hide this button
11365      */
11366     hide: function(){
11367         this.hidden = true;
11368         if(this.el){
11369             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11370         }
11371     },
11372     
11373     /**
11374      * Convenience function for boolean show/hide
11375      * @param {Boolean} visible True to show, false to hide
11376      */
11377     setVisible: function(visible){
11378         if(visible) {
11379             this.show();
11380         }else{
11381             this.hide();
11382         }
11383     },
11384     
11385     /**
11386      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11387      * @param {Boolean} state (optional) Force a particular state
11388      */
11389     toggle : function(state){
11390         state = state === undefined ? !this.pressed : state;
11391         if(state != this.pressed){
11392             if(state){
11393                 this.el.addClass("x-btn-pressed");
11394                 this.pressed = true;
11395                 this.fireEvent("toggle", this, true);
11396             }else{
11397                 this.el.removeClass("x-btn-pressed");
11398                 this.pressed = false;
11399                 this.fireEvent("toggle", this, false);
11400             }
11401             if(this.toggleHandler){
11402                 this.toggleHandler.call(this.scope || this, this, state);
11403             }
11404         }
11405     },
11406     
11407     /**
11408      * Focus the button
11409      */
11410     focus : function(){
11411         this.el.child('button:first').focus();
11412     },
11413     
11414     /**
11415      * Disable this button
11416      */
11417     disable : function(){
11418         if(this.el){
11419             this.el.addClass("x-btn-disabled");
11420         }
11421         this.disabled = true;
11422     },
11423     
11424     /**
11425      * Enable this button
11426      */
11427     enable : function(){
11428         if(this.el){
11429             this.el.removeClass("x-btn-disabled");
11430         }
11431         this.disabled = false;
11432     },
11433
11434     /**
11435      * Convenience function for boolean enable/disable
11436      * @param {Boolean} enabled True to enable, false to disable
11437      */
11438     setDisabled : function(v){
11439         this[v !== true ? "enable" : "disable"]();
11440     },
11441
11442     // private
11443     onClick : function(e)
11444     {
11445         if(e){
11446             e.preventDefault();
11447         }
11448         if(e.button != 0){
11449             return;
11450         }
11451         if(!this.disabled){
11452             if(this.enableToggle){
11453                 this.toggle();
11454             }
11455             if(this.menu && !this.menu.isVisible()){
11456                 this.menu.show(this.el, this.menuAlign);
11457             }
11458             this.fireEvent("click", this, e);
11459             if(this.handler){
11460                 this.el.removeClass("x-btn-over");
11461                 this.handler.call(this.scope || this, this, e);
11462             }
11463         }
11464     },
11465     // private
11466     onMouseOver : function(e){
11467         if(!this.disabled){
11468             this.el.addClass("x-btn-over");
11469             this.fireEvent('mouseover', this, e);
11470         }
11471     },
11472     // private
11473     onMouseOut : function(e){
11474         if(!e.within(this.el,  true)){
11475             this.el.removeClass("x-btn-over");
11476             this.fireEvent('mouseout', this, e);
11477         }
11478     },
11479     // private
11480     onFocus : function(e){
11481         if(!this.disabled){
11482             this.el.addClass("x-btn-focus");
11483         }
11484     },
11485     // private
11486     onBlur : function(e){
11487         this.el.removeClass("x-btn-focus");
11488     },
11489     // private
11490     onMouseDown : function(e){
11491         if(!this.disabled && e.button == 0){
11492             this.el.addClass("x-btn-click");
11493             Roo.get(document).on('mouseup', this.onMouseUp, this);
11494         }
11495     },
11496     // private
11497     onMouseUp : function(e){
11498         if(e.button == 0){
11499             this.el.removeClass("x-btn-click");
11500             Roo.get(document).un('mouseup', this.onMouseUp, this);
11501         }
11502     },
11503     // private
11504     onMenuShow : function(e){
11505         this.el.addClass("x-btn-menu-active");
11506     },
11507     // private
11508     onMenuHide : function(e){
11509         this.el.removeClass("x-btn-menu-active");
11510     }   
11511 });
11512
11513 // Private utility class used by Button
11514 Roo.ButtonToggleMgr = function(){
11515    var groups = {};
11516    
11517    function toggleGroup(btn, state){
11518        if(state){
11519            var g = groups[btn.toggleGroup];
11520            for(var i = 0, l = g.length; i < l; i++){
11521                if(g[i] != btn){
11522                    g[i].toggle(false);
11523                }
11524            }
11525        }
11526    }
11527    
11528    return {
11529        register : function(btn){
11530            if(!btn.toggleGroup){
11531                return;
11532            }
11533            var g = groups[btn.toggleGroup];
11534            if(!g){
11535                g = groups[btn.toggleGroup] = [];
11536            }
11537            g.push(btn);
11538            btn.on("toggle", toggleGroup);
11539        },
11540        
11541        unregister : function(btn){
11542            if(!btn.toggleGroup){
11543                return;
11544            }
11545            var g = groups[btn.toggleGroup];
11546            if(g){
11547                g.remove(btn);
11548                btn.un("toggle", toggleGroup);
11549            }
11550        }
11551    };
11552 }();/*
11553  * Based on:
11554  * Ext JS Library 1.1.1
11555  * Copyright(c) 2006-2007, Ext JS, LLC.
11556  *
11557  * Originally Released Under LGPL - original licence link has changed is not relivant.
11558  *
11559  * Fork - LGPL
11560  * <script type="text/javascript">
11561  */
11562  
11563 /**
11564  * @class Roo.SplitButton
11565  * @extends Roo.Button
11566  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
11567  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
11568  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
11569  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
11570  * @cfg {String} arrowTooltip The title attribute of the arrow
11571  * @constructor
11572  * Create a new menu button
11573  * @param {String/HTMLElement/Element} renderTo The element to append the button to
11574  * @param {Object} config The config object
11575  */
11576 Roo.SplitButton = function(renderTo, config){
11577     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
11578     /**
11579      * @event arrowclick
11580      * Fires when this button's arrow is clicked
11581      * @param {SplitButton} this
11582      * @param {EventObject} e The click event
11583      */
11584     this.addEvents({"arrowclick":true});
11585 };
11586
11587 Roo.extend(Roo.SplitButton, Roo.Button, {
11588     render : function(renderTo){
11589         // this is one sweet looking template!
11590         var tpl = new Roo.Template(
11591             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
11592             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
11593             '<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>',
11594             "</tbody></table></td><td>",
11595             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
11596             '<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>',
11597             "</tbody></table></td></tr></table>"
11598         );
11599         var btn = tpl.append(renderTo, [this.text, this.type], true);
11600         var btnEl = btn.child("button");
11601         if(this.cls){
11602             btn.addClass(this.cls);
11603         }
11604         if(this.icon){
11605             btnEl.setStyle('background-image', 'url(' +this.icon +')');
11606         }
11607         if(this.iconCls){
11608             btnEl.addClass(this.iconCls);
11609             if(!this.cls){
11610                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11611             }
11612         }
11613         this.el = btn;
11614         if(this.handleMouseEvents){
11615             btn.on("mouseover", this.onMouseOver, this);
11616             btn.on("mouseout", this.onMouseOut, this);
11617             btn.on("mousedown", this.onMouseDown, this);
11618             btn.on("mouseup", this.onMouseUp, this);
11619         }
11620         btn.on(this.clickEvent, this.onClick, this);
11621         if(this.tooltip){
11622             if(typeof this.tooltip == 'object'){
11623                 Roo.QuickTips.tips(Roo.apply({
11624                       target: btnEl.id
11625                 }, this.tooltip));
11626             } else {
11627                 btnEl.dom[this.tooltipType] = this.tooltip;
11628             }
11629         }
11630         if(this.arrowTooltip){
11631             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
11632         }
11633         if(this.hidden){
11634             this.hide();
11635         }
11636         if(this.disabled){
11637             this.disable();
11638         }
11639         if(this.pressed){
11640             this.el.addClass("x-btn-pressed");
11641         }
11642         if(Roo.isIE && !Roo.isIE7){
11643             this.autoWidth.defer(1, this);
11644         }else{
11645             this.autoWidth();
11646         }
11647         if(this.menu){
11648             this.menu.on("show", this.onMenuShow, this);
11649             this.menu.on("hide", this.onMenuHide, this);
11650         }
11651         this.fireEvent('render', this);
11652     },
11653
11654     // private
11655     autoWidth : function(){
11656         if(this.el){
11657             var tbl = this.el.child("table:first");
11658             var tbl2 = this.el.child("table:last");
11659             this.el.setWidth("auto");
11660             tbl.setWidth("auto");
11661             if(Roo.isIE7 && Roo.isStrict){
11662                 var ib = this.el.child('button:first');
11663                 if(ib && ib.getWidth() > 20){
11664                     ib.clip();
11665                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11666                 }
11667             }
11668             if(this.minWidth){
11669                 if(this.hidden){
11670                     this.el.beginMeasure();
11671                 }
11672                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
11673                     tbl.setWidth(this.minWidth-tbl2.getWidth());
11674                 }
11675                 if(this.hidden){
11676                     this.el.endMeasure();
11677                 }
11678             }
11679             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
11680         } 
11681     },
11682     /**
11683      * Sets this button's click handler
11684      * @param {Function} handler The function to call when the button is clicked
11685      * @param {Object} scope (optional) Scope for the function passed above
11686      */
11687     setHandler : function(handler, scope){
11688         this.handler = handler;
11689         this.scope = scope;  
11690     },
11691     
11692     /**
11693      * Sets this button's arrow click handler
11694      * @param {Function} handler The function to call when the arrow is clicked
11695      * @param {Object} scope (optional) Scope for the function passed above
11696      */
11697     setArrowHandler : function(handler, scope){
11698         this.arrowHandler = handler;
11699         this.scope = scope;  
11700     },
11701     
11702     /**
11703      * Focus the button
11704      */
11705     focus : function(){
11706         if(this.el){
11707             this.el.child("button:first").focus();
11708         }
11709     },
11710
11711     // private
11712     onClick : function(e){
11713         e.preventDefault();
11714         if(!this.disabled){
11715             if(e.getTarget(".x-btn-menu-arrow-wrap")){
11716                 if(this.menu && !this.menu.isVisible()){
11717                     this.menu.show(this.el, this.menuAlign);
11718                 }
11719                 this.fireEvent("arrowclick", this, e);
11720                 if(this.arrowHandler){
11721                     this.arrowHandler.call(this.scope || this, this, e);
11722                 }
11723             }else{
11724                 this.fireEvent("click", this, e);
11725                 if(this.handler){
11726                     this.handler.call(this.scope || this, this, e);
11727                 }
11728             }
11729         }
11730     },
11731     // private
11732     onMouseDown : function(e){
11733         if(!this.disabled){
11734             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
11735         }
11736     },
11737     // private
11738     onMouseUp : function(e){
11739         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
11740     }   
11741 });
11742
11743
11744 // backwards compat
11745 Roo.MenuButton = Roo.SplitButton;/*
11746  * Based on:
11747  * Ext JS Library 1.1.1
11748  * Copyright(c) 2006-2007, Ext JS, LLC.
11749  *
11750  * Originally Released Under LGPL - original licence link has changed is not relivant.
11751  *
11752  * Fork - LGPL
11753  * <script type="text/javascript">
11754  */
11755
11756 /**
11757  * @class Roo.Toolbar
11758  * Basic Toolbar class.
11759  * @constructor
11760  * Creates a new Toolbar
11761  * @param {Object} container The config object
11762  */ 
11763 Roo.Toolbar = function(container, buttons, config)
11764 {
11765     /// old consturctor format still supported..
11766     if(container instanceof Array){ // omit the container for later rendering
11767         buttons = container;
11768         config = buttons;
11769         container = null;
11770     }
11771     if (typeof(container) == 'object' && container.xtype) {
11772         config = container;
11773         container = config.container;
11774         buttons = config.buttons || []; // not really - use items!!
11775     }
11776     var xitems = [];
11777     if (config && config.items) {
11778         xitems = config.items;
11779         delete config.items;
11780     }
11781     Roo.apply(this, config);
11782     this.buttons = buttons;
11783     
11784     if(container){
11785         this.render(container);
11786     }
11787     this.xitems = xitems;
11788     Roo.each(xitems, function(b) {
11789         this.add(b);
11790     }, this);
11791     
11792 };
11793
11794 Roo.Toolbar.prototype = {
11795     /**
11796      * @cfg {Array} items
11797      * array of button configs or elements to add (will be converted to a MixedCollection)
11798      */
11799     
11800     /**
11801      * @cfg {String/HTMLElement/Element} container
11802      * The id or element that will contain the toolbar
11803      */
11804     // private
11805     render : function(ct){
11806         this.el = Roo.get(ct);
11807         if(this.cls){
11808             this.el.addClass(this.cls);
11809         }
11810         // using a table allows for vertical alignment
11811         // 100% width is needed by Safari...
11812         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
11813         this.tr = this.el.child("tr", true);
11814         var autoId = 0;
11815         this.items = new Roo.util.MixedCollection(false, function(o){
11816             return o.id || ("item" + (++autoId));
11817         });
11818         if(this.buttons){
11819             this.add.apply(this, this.buttons);
11820             delete this.buttons;
11821         }
11822     },
11823
11824     /**
11825      * Adds element(s) to the toolbar -- this function takes a variable number of 
11826      * arguments of mixed type and adds them to the toolbar.
11827      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
11828      * <ul>
11829      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
11830      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
11831      * <li>Field: Any form field (equivalent to {@link #addField})</li>
11832      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
11833      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
11834      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
11835      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
11836      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
11837      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
11838      * </ul>
11839      * @param {Mixed} arg2
11840      * @param {Mixed} etc.
11841      */
11842     add : function(){
11843         var a = arguments, l = a.length;
11844         for(var i = 0; i < l; i++){
11845             this._add(a[i]);
11846         }
11847     },
11848     // private..
11849     _add : function(el) {
11850         
11851         if (el.xtype) {
11852             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
11853         }
11854         
11855         if (el.applyTo){ // some kind of form field
11856             return this.addField(el);
11857         } 
11858         if (el.render){ // some kind of Toolbar.Item
11859             return this.addItem(el);
11860         }
11861         if (typeof el == "string"){ // string
11862             if(el == "separator" || el == "-"){
11863                 return this.addSeparator();
11864             }
11865             if (el == " "){
11866                 return this.addSpacer();
11867             }
11868             if(el == "->"){
11869                 return this.addFill();
11870             }
11871             return this.addText(el);
11872             
11873         }
11874         if(el.tagName){ // element
11875             return this.addElement(el);
11876         }
11877         if(typeof el == "object"){ // must be button config?
11878             return this.addButton(el);
11879         }
11880         // and now what?!?!
11881         return false;
11882         
11883     },
11884     
11885     /**
11886      * Add an Xtype element
11887      * @param {Object} xtype Xtype Object
11888      * @return {Object} created Object
11889      */
11890     addxtype : function(e){
11891         return this.add(e);  
11892     },
11893     
11894     /**
11895      * Returns the Element for this toolbar.
11896      * @return {Roo.Element}
11897      */
11898     getEl : function(){
11899         return this.el;  
11900     },
11901     
11902     /**
11903      * Adds a separator
11904      * @return {Roo.Toolbar.Item} The separator item
11905      */
11906     addSeparator : function(){
11907         return this.addItem(new Roo.Toolbar.Separator());
11908     },
11909
11910     /**
11911      * Adds a spacer element
11912      * @return {Roo.Toolbar.Spacer} The spacer item
11913      */
11914     addSpacer : function(){
11915         return this.addItem(new Roo.Toolbar.Spacer());
11916     },
11917
11918     /**
11919      * Adds a fill element that forces subsequent additions to the right side of the toolbar
11920      * @return {Roo.Toolbar.Fill} The fill item
11921      */
11922     addFill : function(){
11923         return this.addItem(new Roo.Toolbar.Fill());
11924     },
11925
11926     /**
11927      * Adds any standard HTML element to the toolbar
11928      * @param {String/HTMLElement/Element} el The element or id of the element to add
11929      * @return {Roo.Toolbar.Item} The element's item
11930      */
11931     addElement : function(el){
11932         return this.addItem(new Roo.Toolbar.Item(el));
11933     },
11934     /**
11935      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
11936      * @type Roo.util.MixedCollection  
11937      */
11938     items : false,
11939      
11940     /**
11941      * Adds any Toolbar.Item or subclass
11942      * @param {Roo.Toolbar.Item} item
11943      * @return {Roo.Toolbar.Item} The item
11944      */
11945     addItem : function(item){
11946         var td = this.nextBlock();
11947         item.render(td);
11948         this.items.add(item);
11949         return item;
11950     },
11951     
11952     /**
11953      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
11954      * @param {Object/Array} config A button config or array of configs
11955      * @return {Roo.Toolbar.Button/Array}
11956      */
11957     addButton : function(config){
11958         if(config instanceof Array){
11959             var buttons = [];
11960             for(var i = 0, len = config.length; i < len; i++) {
11961                 buttons.push(this.addButton(config[i]));
11962             }
11963             return buttons;
11964         }
11965         var b = config;
11966         if(!(config instanceof Roo.Toolbar.Button)){
11967             b = config.split ?
11968                 new Roo.Toolbar.SplitButton(config) :
11969                 new Roo.Toolbar.Button(config);
11970         }
11971         var td = this.nextBlock();
11972         b.render(td);
11973         this.items.add(b);
11974         return b;
11975     },
11976     
11977     /**
11978      * Adds text to the toolbar
11979      * @param {String} text The text to add
11980      * @return {Roo.Toolbar.Item} The element's item
11981      */
11982     addText : function(text){
11983         return this.addItem(new Roo.Toolbar.TextItem(text));
11984     },
11985     
11986     /**
11987      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
11988      * @param {Number} index The index where the item is to be inserted
11989      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
11990      * @return {Roo.Toolbar.Button/Item}
11991      */
11992     insertButton : function(index, item){
11993         if(item instanceof Array){
11994             var buttons = [];
11995             for(var i = 0, len = item.length; i < len; i++) {
11996                buttons.push(this.insertButton(index + i, item[i]));
11997             }
11998             return buttons;
11999         }
12000         if (!(item instanceof Roo.Toolbar.Button)){
12001            item = new Roo.Toolbar.Button(item);
12002         }
12003         var td = document.createElement("td");
12004         this.tr.insertBefore(td, this.tr.childNodes[index]);
12005         item.render(td);
12006         this.items.insert(index, item);
12007         return item;
12008     },
12009     
12010     /**
12011      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12012      * @param {Object} config
12013      * @return {Roo.Toolbar.Item} The element's item
12014      */
12015     addDom : function(config, returnEl){
12016         var td = this.nextBlock();
12017         Roo.DomHelper.overwrite(td, config);
12018         var ti = new Roo.Toolbar.Item(td.firstChild);
12019         ti.render(td);
12020         this.items.add(ti);
12021         return ti;
12022     },
12023
12024     /**
12025      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12026      * @type Roo.util.MixedCollection  
12027      */
12028     fields : false,
12029     
12030     /**
12031      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12032      * Note: the field should not have been rendered yet. For a field that has already been
12033      * rendered, use {@link #addElement}.
12034      * @param {Roo.form.Field} field
12035      * @return {Roo.ToolbarItem}
12036      */
12037      
12038       
12039     addField : function(field) {
12040         if (!this.fields) {
12041             var autoId = 0;
12042             this.fields = new Roo.util.MixedCollection(false, function(o){
12043                 return o.id || ("item" + (++autoId));
12044             });
12045
12046         }
12047         
12048         var td = this.nextBlock();
12049         field.render(td);
12050         var ti = new Roo.Toolbar.Item(td.firstChild);
12051         ti.render(td);
12052         this.items.add(ti);
12053         this.fields.add(field);
12054         return ti;
12055     },
12056     /**
12057      * Hide the toolbar
12058      * @method hide
12059      */
12060      
12061       
12062     hide : function()
12063     {
12064         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12065         this.el.child('div').hide();
12066     },
12067     /**
12068      * Show the toolbar
12069      * @method show
12070      */
12071     show : function()
12072     {
12073         this.el.child('div').show();
12074     },
12075       
12076     // private
12077     nextBlock : function(){
12078         var td = document.createElement("td");
12079         this.tr.appendChild(td);
12080         return td;
12081     },
12082
12083     // private
12084     destroy : function(){
12085         if(this.items){ // rendered?
12086             Roo.destroy.apply(Roo, this.items.items);
12087         }
12088         if(this.fields){ // rendered?
12089             Roo.destroy.apply(Roo, this.fields.items);
12090         }
12091         Roo.Element.uncache(this.el, this.tr);
12092     }
12093 };
12094
12095 /**
12096  * @class Roo.Toolbar.Item
12097  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12098  * @constructor
12099  * Creates a new Item
12100  * @param {HTMLElement} el 
12101  */
12102 Roo.Toolbar.Item = function(el){
12103     var cfg = {};
12104     if (typeof (el.xtype) != 'undefined') {
12105         cfg = el;
12106         el = cfg.el;
12107     }
12108     
12109     this.el = Roo.getDom(el);
12110     this.id = Roo.id(this.el);
12111     this.hidden = false;
12112     
12113     this.addEvents({
12114          /**
12115              * @event render
12116              * Fires when the button is rendered
12117              * @param {Button} this
12118              */
12119         'render': true
12120     });
12121     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
12122 };
12123 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
12124 //Roo.Toolbar.Item.prototype = {
12125     
12126     /**
12127      * Get this item's HTML Element
12128      * @return {HTMLElement}
12129      */
12130     getEl : function(){
12131        return this.el;  
12132     },
12133
12134     // private
12135     render : function(td){
12136         
12137          this.td = td;
12138         td.appendChild(this.el);
12139         
12140         this.fireEvent('render', this);
12141     },
12142     
12143     /**
12144      * Removes and destroys this item.
12145      */
12146     destroy : function(){
12147         this.td.parentNode.removeChild(this.td);
12148     },
12149     
12150     /**
12151      * Shows this item.
12152      */
12153     show: function(){
12154         this.hidden = false;
12155         this.td.style.display = "";
12156     },
12157     
12158     /**
12159      * Hides this item.
12160      */
12161     hide: function(){
12162         this.hidden = true;
12163         this.td.style.display = "none";
12164     },
12165     
12166     /**
12167      * Convenience function for boolean show/hide.
12168      * @param {Boolean} visible true to show/false to hide
12169      */
12170     setVisible: function(visible){
12171         if(visible) {
12172             this.show();
12173         }else{
12174             this.hide();
12175         }
12176     },
12177     
12178     /**
12179      * Try to focus this item.
12180      */
12181     focus : function(){
12182         Roo.fly(this.el).focus();
12183     },
12184     
12185     /**
12186      * Disables this item.
12187      */
12188     disable : function(){
12189         Roo.fly(this.td).addClass("x-item-disabled");
12190         this.disabled = true;
12191         this.el.disabled = true;
12192     },
12193     
12194     /**
12195      * Enables this item.
12196      */
12197     enable : function(){
12198         Roo.fly(this.td).removeClass("x-item-disabled");
12199         this.disabled = false;
12200         this.el.disabled = false;
12201     }
12202 });
12203
12204
12205 /**
12206  * @class Roo.Toolbar.Separator
12207  * @extends Roo.Toolbar.Item
12208  * A simple toolbar separator class
12209  * @constructor
12210  * Creates a new Separator
12211  */
12212 Roo.Toolbar.Separator = function(cfg){
12213     
12214     var s = document.createElement("span");
12215     s.className = "ytb-sep";
12216     if (cfg) {
12217         cfg.el = s;
12218     }
12219     
12220     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
12221 };
12222 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12223     enable:Roo.emptyFn,
12224     disable:Roo.emptyFn,
12225     focus:Roo.emptyFn
12226 });
12227
12228 /**
12229  * @class Roo.Toolbar.Spacer
12230  * @extends Roo.Toolbar.Item
12231  * A simple element that adds extra horizontal space to a toolbar.
12232  * @constructor
12233  * Creates a new Spacer
12234  */
12235 Roo.Toolbar.Spacer = function(cfg){
12236     var s = document.createElement("div");
12237     s.className = "ytb-spacer";
12238     if (cfg) {
12239         cfg.el = s;
12240     }
12241     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
12242 };
12243 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12244     enable:Roo.emptyFn,
12245     disable:Roo.emptyFn,
12246     focus:Roo.emptyFn
12247 });
12248
12249 /**
12250  * @class Roo.Toolbar.Fill
12251  * @extends Roo.Toolbar.Spacer
12252  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12253  * @constructor
12254  * Creates a new Spacer
12255  */
12256 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12257     // private
12258     render : function(td){
12259         td.style.width = '100%';
12260         Roo.Toolbar.Fill.superclass.render.call(this, td);
12261     }
12262 });
12263
12264 /**
12265  * @class Roo.Toolbar.TextItem
12266  * @extends Roo.Toolbar.Item
12267  * A simple class that renders text directly into a toolbar.
12268  * @constructor
12269  * Creates a new TextItem
12270  * @param {String} text
12271  */
12272 Roo.Toolbar.TextItem = function(cfg){
12273     var  text = cfg || "";
12274     if (typeof(cfg) == 'object') {
12275         text = cfg.text || "";
12276     }  else {
12277         cfg = null;
12278     }
12279     var s = document.createElement("span");
12280     s.className = "ytb-text";
12281     s.innerHTML = text;
12282     if (cfg) {
12283         cfg.el  = s;
12284     }
12285     
12286     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
12287 };
12288 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12289     
12290      
12291     enable:Roo.emptyFn,
12292     disable:Roo.emptyFn,
12293     focus:Roo.emptyFn
12294 });
12295
12296 /**
12297  * @class Roo.Toolbar.Button
12298  * @extends Roo.Button
12299  * A button that renders into a toolbar.
12300  * @constructor
12301  * Creates a new Button
12302  * @param {Object} config A standard {@link Roo.Button} config object
12303  */
12304 Roo.Toolbar.Button = function(config){
12305     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12306 };
12307 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12308     render : function(td){
12309         this.td = td;
12310         Roo.Toolbar.Button.superclass.render.call(this, td);
12311     },
12312     
12313     /**
12314      * Removes and destroys this button
12315      */
12316     destroy : function(){
12317         Roo.Toolbar.Button.superclass.destroy.call(this);
12318         this.td.parentNode.removeChild(this.td);
12319     },
12320     
12321     /**
12322      * Shows this button
12323      */
12324     show: function(){
12325         this.hidden = false;
12326         this.td.style.display = "";
12327     },
12328     
12329     /**
12330      * Hides this button
12331      */
12332     hide: function(){
12333         this.hidden = true;
12334         this.td.style.display = "none";
12335     },
12336
12337     /**
12338      * Disables this item
12339      */
12340     disable : function(){
12341         Roo.fly(this.td).addClass("x-item-disabled");
12342         this.disabled = true;
12343     },
12344
12345     /**
12346      * Enables this item
12347      */
12348     enable : function(){
12349         Roo.fly(this.td).removeClass("x-item-disabled");
12350         this.disabled = false;
12351     }
12352 });
12353 // backwards compat
12354 Roo.ToolbarButton = Roo.Toolbar.Button;
12355
12356 /**
12357  * @class Roo.Toolbar.SplitButton
12358  * @extends Roo.SplitButton
12359  * A menu button that renders into a toolbar.
12360  * @constructor
12361  * Creates a new SplitButton
12362  * @param {Object} config A standard {@link Roo.SplitButton} config object
12363  */
12364 Roo.Toolbar.SplitButton = function(config){
12365     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12366 };
12367 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12368     render : function(td){
12369         this.td = td;
12370         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12371     },
12372     
12373     /**
12374      * Removes and destroys this button
12375      */
12376     destroy : function(){
12377         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12378         this.td.parentNode.removeChild(this.td);
12379     },
12380     
12381     /**
12382      * Shows this button
12383      */
12384     show: function(){
12385         this.hidden = false;
12386         this.td.style.display = "";
12387     },
12388     
12389     /**
12390      * Hides this button
12391      */
12392     hide: function(){
12393         this.hidden = true;
12394         this.td.style.display = "none";
12395     }
12396 });
12397
12398 // backwards compat
12399 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12400  * Based on:
12401  * Ext JS Library 1.1.1
12402  * Copyright(c) 2006-2007, Ext JS, LLC.
12403  *
12404  * Originally Released Under LGPL - original licence link has changed is not relivant.
12405  *
12406  * Fork - LGPL
12407  * <script type="text/javascript">
12408  */
12409  
12410 /**
12411  * @class Roo.PagingToolbar
12412  * @extends Roo.Toolbar
12413  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12414  * @constructor
12415  * Create a new PagingToolbar
12416  * @param {Object} config The config object
12417  */
12418 Roo.PagingToolbar = function(el, ds, config)
12419 {
12420     // old args format still supported... - xtype is prefered..
12421     if (typeof(el) == 'object' && el.xtype) {
12422         // created from xtype...
12423         config = el;
12424         ds = el.dataSource;
12425         el = config.container;
12426     }
12427     var items = [];
12428     if (config.items) {
12429         items = config.items;
12430         config.items = [];
12431     }
12432     
12433     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12434     this.ds = ds;
12435     this.cursor = 0;
12436     this.renderButtons(this.el);
12437     this.bind(ds);
12438     
12439     // supprot items array.
12440    
12441     Roo.each(items, function(e) {
12442         this.add(Roo.factory(e));
12443     },this);
12444     
12445 };
12446
12447 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12448     /**
12449      * @cfg {Roo.data.Store} dataSource
12450      * The underlying data store providing the paged data
12451      */
12452     /**
12453      * @cfg {String/HTMLElement/Element} container
12454      * container The id or element that will contain the toolbar
12455      */
12456     /**
12457      * @cfg {Boolean} displayInfo
12458      * True to display the displayMsg (defaults to false)
12459      */
12460     /**
12461      * @cfg {Number} pageSize
12462      * The number of records to display per page (defaults to 20)
12463      */
12464     pageSize: 20,
12465     /**
12466      * @cfg {String} displayMsg
12467      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12468      */
12469     displayMsg : 'Displaying {0} - {1} of {2}',
12470     /**
12471      * @cfg {String} emptyMsg
12472      * The message to display when no records are found (defaults to "No data to display")
12473      */
12474     emptyMsg : 'No data to display',
12475     /**
12476      * Customizable piece of the default paging text (defaults to "Page")
12477      * @type String
12478      */
12479     beforePageText : "Page",
12480     /**
12481      * Customizable piece of the default paging text (defaults to "of %0")
12482      * @type String
12483      */
12484     afterPageText : "of {0}",
12485     /**
12486      * Customizable piece of the default paging text (defaults to "First Page")
12487      * @type String
12488      */
12489     firstText : "First Page",
12490     /**
12491      * Customizable piece of the default paging text (defaults to "Previous Page")
12492      * @type String
12493      */
12494     prevText : "Previous Page",
12495     /**
12496      * Customizable piece of the default paging text (defaults to "Next Page")
12497      * @type String
12498      */
12499     nextText : "Next Page",
12500     /**
12501      * Customizable piece of the default paging text (defaults to "Last Page")
12502      * @type String
12503      */
12504     lastText : "Last Page",
12505     /**
12506      * Customizable piece of the default paging text (defaults to "Refresh")
12507      * @type String
12508      */
12509     refreshText : "Refresh",
12510
12511     // private
12512     renderButtons : function(el){
12513         Roo.PagingToolbar.superclass.render.call(this, el);
12514         this.first = this.addButton({
12515             tooltip: this.firstText,
12516             cls: "x-btn-icon x-grid-page-first",
12517             disabled: true,
12518             handler: this.onClick.createDelegate(this, ["first"])
12519         });
12520         this.prev = this.addButton({
12521             tooltip: this.prevText,
12522             cls: "x-btn-icon x-grid-page-prev",
12523             disabled: true,
12524             handler: this.onClick.createDelegate(this, ["prev"])
12525         });
12526         //this.addSeparator();
12527         this.add(this.beforePageText);
12528         this.field = Roo.get(this.addDom({
12529            tag: "input",
12530            type: "text",
12531            size: "3",
12532            value: "1",
12533            cls: "x-grid-page-number"
12534         }).el);
12535         this.field.on("keydown", this.onPagingKeydown, this);
12536         this.field.on("focus", function(){this.dom.select();});
12537         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12538         this.field.setHeight(18);
12539         //this.addSeparator();
12540         this.next = this.addButton({
12541             tooltip: this.nextText,
12542             cls: "x-btn-icon x-grid-page-next",
12543             disabled: true,
12544             handler: this.onClick.createDelegate(this, ["next"])
12545         });
12546         this.last = this.addButton({
12547             tooltip: this.lastText,
12548             cls: "x-btn-icon x-grid-page-last",
12549             disabled: true,
12550             handler: this.onClick.createDelegate(this, ["last"])
12551         });
12552         //this.addSeparator();
12553         this.loading = this.addButton({
12554             tooltip: this.refreshText,
12555             cls: "x-btn-icon x-grid-loading",
12556             handler: this.onClick.createDelegate(this, ["refresh"])
12557         });
12558
12559         if(this.displayInfo){
12560             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12561         }
12562     },
12563
12564     // private
12565     updateInfo : function(){
12566         if(this.displayEl){
12567             var count = this.ds.getCount();
12568             var msg = count == 0 ?
12569                 this.emptyMsg :
12570                 String.format(
12571                     this.displayMsg,
12572                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
12573                 );
12574             this.displayEl.update(msg);
12575         }
12576     },
12577
12578     // private
12579     onLoad : function(ds, r, o){
12580        this.cursor = o.params ? o.params.start : 0;
12581        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12582
12583        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
12584        this.field.dom.value = ap;
12585        this.first.setDisabled(ap == 1);
12586        this.prev.setDisabled(ap == 1);
12587        this.next.setDisabled(ap == ps);
12588        this.last.setDisabled(ap == ps);
12589        this.loading.enable();
12590        this.updateInfo();
12591     },
12592
12593     // private
12594     getPageData : function(){
12595         var total = this.ds.getTotalCount();
12596         return {
12597             total : total,
12598             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
12599             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
12600         };
12601     },
12602
12603     // private
12604     onLoadError : function(){
12605         this.loading.enable();
12606     },
12607
12608     // private
12609     onPagingKeydown : function(e){
12610         var k = e.getKey();
12611         var d = this.getPageData();
12612         if(k == e.RETURN){
12613             var v = this.field.dom.value, pageNum;
12614             if(!v || isNaN(pageNum = parseInt(v, 10))){
12615                 this.field.dom.value = d.activePage;
12616                 return;
12617             }
12618             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
12619             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12620             e.stopEvent();
12621         }
12622         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))
12623         {
12624           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
12625           this.field.dom.value = pageNum;
12626           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
12627           e.stopEvent();
12628         }
12629         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12630         {
12631           var v = this.field.dom.value, pageNum; 
12632           var increment = (e.shiftKey) ? 10 : 1;
12633           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
12634             increment *= -1;
12635           }
12636           if(!v || isNaN(pageNum = parseInt(v, 10))) {
12637             this.field.dom.value = d.activePage;
12638             return;
12639           }
12640           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
12641           {
12642             this.field.dom.value = parseInt(v, 10) + increment;
12643             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
12644             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12645           }
12646           e.stopEvent();
12647         }
12648     },
12649
12650     // private
12651     beforeLoad : function(){
12652         if(this.loading){
12653             this.loading.disable();
12654         }
12655     },
12656
12657     // private
12658     onClick : function(which){
12659         var ds = this.ds;
12660         switch(which){
12661             case "first":
12662                 ds.load({params:{start: 0, limit: this.pageSize}});
12663             break;
12664             case "prev":
12665                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
12666             break;
12667             case "next":
12668                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
12669             break;
12670             case "last":
12671                 var total = ds.getTotalCount();
12672                 var extra = total % this.pageSize;
12673                 var lastStart = extra ? (total - extra) : total-this.pageSize;
12674                 ds.load({params:{start: lastStart, limit: this.pageSize}});
12675             break;
12676             case "refresh":
12677                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
12678             break;
12679         }
12680     },
12681
12682     /**
12683      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
12684      * @param {Roo.data.Store} store The data store to unbind
12685      */
12686     unbind : function(ds){
12687         ds.un("beforeload", this.beforeLoad, this);
12688         ds.un("load", this.onLoad, this);
12689         ds.un("loadexception", this.onLoadError, this);
12690         ds.un("remove", this.updateInfo, this);
12691         ds.un("add", this.updateInfo, this);
12692         this.ds = undefined;
12693     },
12694
12695     /**
12696      * Binds the paging toolbar to the specified {@link Roo.data.Store}
12697      * @param {Roo.data.Store} store The data store to bind
12698      */
12699     bind : function(ds){
12700         ds.on("beforeload", this.beforeLoad, this);
12701         ds.on("load", this.onLoad, this);
12702         ds.on("loadexception", this.onLoadError, this);
12703         ds.on("remove", this.updateInfo, this);
12704         ds.on("add", this.updateInfo, this);
12705         this.ds = ds;
12706     }
12707 });/*
12708  * Based on:
12709  * Ext JS Library 1.1.1
12710  * Copyright(c) 2006-2007, Ext JS, LLC.
12711  *
12712  * Originally Released Under LGPL - original licence link has changed is not relivant.
12713  *
12714  * Fork - LGPL
12715  * <script type="text/javascript">
12716  */
12717
12718 /**
12719  * @class Roo.Resizable
12720  * @extends Roo.util.Observable
12721  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
12722  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
12723  * 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
12724  * the element will be wrapped for you automatically.</p>
12725  * <p>Here is the list of valid resize handles:</p>
12726  * <pre>
12727 Value   Description
12728 ------  -------------------
12729  'n'     north
12730  's'     south
12731  'e'     east
12732  'w'     west
12733  'nw'    northwest
12734  'sw'    southwest
12735  'se'    southeast
12736  'ne'    northeast
12737  'hd'    horizontal drag
12738  'all'   all
12739 </pre>
12740  * <p>Here's an example showing the creation of a typical Resizable:</p>
12741  * <pre><code>
12742 var resizer = new Roo.Resizable("element-id", {
12743     handles: 'all',
12744     minWidth: 200,
12745     minHeight: 100,
12746     maxWidth: 500,
12747     maxHeight: 400,
12748     pinned: true
12749 });
12750 resizer.on("resize", myHandler);
12751 </code></pre>
12752  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
12753  * resizer.east.setDisplayed(false);</p>
12754  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
12755  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
12756  * resize operation's new size (defaults to [0, 0])
12757  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
12758  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
12759  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
12760  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
12761  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
12762  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
12763  * @cfg {Number} width The width of the element in pixels (defaults to null)
12764  * @cfg {Number} height The height of the element in pixels (defaults to null)
12765  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
12766  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
12767  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
12768  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
12769  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
12770  * in favor of the handles config option (defaults to false)
12771  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
12772  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
12773  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
12774  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
12775  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
12776  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
12777  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
12778  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
12779  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
12780  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
12781  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
12782  * @constructor
12783  * Create a new resizable component
12784  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
12785  * @param {Object} config configuration options
12786   */
12787 Roo.Resizable = function(el, config)
12788 {
12789     this.el = Roo.get(el);
12790
12791     if(config && config.wrap){
12792         config.resizeChild = this.el;
12793         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
12794         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
12795         this.el.setStyle("overflow", "hidden");
12796         this.el.setPositioning(config.resizeChild.getPositioning());
12797         config.resizeChild.clearPositioning();
12798         if(!config.width || !config.height){
12799             var csize = config.resizeChild.getSize();
12800             this.el.setSize(csize.width, csize.height);
12801         }
12802         if(config.pinned && !config.adjustments){
12803             config.adjustments = "auto";
12804         }
12805     }
12806
12807     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
12808     this.proxy.unselectable();
12809     this.proxy.enableDisplayMode('block');
12810
12811     Roo.apply(this, config);
12812
12813     if(this.pinned){
12814         this.disableTrackOver = true;
12815         this.el.addClass("x-resizable-pinned");
12816     }
12817     // if the element isn't positioned, make it relative
12818     var position = this.el.getStyle("position");
12819     if(position != "absolute" && position != "fixed"){
12820         this.el.setStyle("position", "relative");
12821     }
12822     if(!this.handles){ // no handles passed, must be legacy style
12823         this.handles = 's,e,se';
12824         if(this.multiDirectional){
12825             this.handles += ',n,w';
12826         }
12827     }
12828     if(this.handles == "all"){
12829         this.handles = "n s e w ne nw se sw";
12830     }
12831     var hs = this.handles.split(/\s*?[,;]\s*?| /);
12832     var ps = Roo.Resizable.positions;
12833     for(var i = 0, len = hs.length; i < len; i++){
12834         if(hs[i] && ps[hs[i]]){
12835             var pos = ps[hs[i]];
12836             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
12837         }
12838     }
12839     // legacy
12840     this.corner = this.southeast;
12841     
12842     // updateBox = the box can move..
12843     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
12844         this.updateBox = true;
12845     }
12846
12847     this.activeHandle = null;
12848
12849     if(this.resizeChild){
12850         if(typeof this.resizeChild == "boolean"){
12851             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
12852         }else{
12853             this.resizeChild = Roo.get(this.resizeChild, true);
12854         }
12855     }
12856     
12857     if(this.adjustments == "auto"){
12858         var rc = this.resizeChild;
12859         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
12860         if(rc && (hw || hn)){
12861             rc.position("relative");
12862             rc.setLeft(hw ? hw.el.getWidth() : 0);
12863             rc.setTop(hn ? hn.el.getHeight() : 0);
12864         }
12865         this.adjustments = [
12866             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
12867             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
12868         ];
12869     }
12870
12871     if(this.draggable){
12872         this.dd = this.dynamic ?
12873             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
12874         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
12875     }
12876
12877     // public events
12878     this.addEvents({
12879         /**
12880          * @event beforeresize
12881          * Fired before resize is allowed. Set enabled to false to cancel resize.
12882          * @param {Roo.Resizable} this
12883          * @param {Roo.EventObject} e The mousedown event
12884          */
12885         "beforeresize" : true,
12886         /**
12887          * @event resizing
12888          * Fired a resizing.
12889          * @param {Roo.Resizable} this
12890          * @param {Number} x The new x position
12891          * @param {Number} y The new y position
12892          * @param {Number} w The new w width
12893          * @param {Number} h The new h hight
12894          * @param {Roo.EventObject} e The mouseup event
12895          */
12896         "resizing" : true,
12897         /**
12898          * @event resize
12899          * Fired after a resize.
12900          * @param {Roo.Resizable} this
12901          * @param {Number} width The new width
12902          * @param {Number} height The new height
12903          * @param {Roo.EventObject} e The mouseup event
12904          */
12905         "resize" : true
12906     });
12907
12908     if(this.width !== null && this.height !== null){
12909         this.resizeTo(this.width, this.height);
12910     }else{
12911         this.updateChildSize();
12912     }
12913     if(Roo.isIE){
12914         this.el.dom.style.zoom = 1;
12915     }
12916     Roo.Resizable.superclass.constructor.call(this);
12917 };
12918
12919 Roo.extend(Roo.Resizable, Roo.util.Observable, {
12920         resizeChild : false,
12921         adjustments : [0, 0],
12922         minWidth : 5,
12923         minHeight : 5,
12924         maxWidth : 10000,
12925         maxHeight : 10000,
12926         enabled : true,
12927         animate : false,
12928         duration : .35,
12929         dynamic : false,
12930         handles : false,
12931         multiDirectional : false,
12932         disableTrackOver : false,
12933         easing : 'easeOutStrong',
12934         widthIncrement : 0,
12935         heightIncrement : 0,
12936         pinned : false,
12937         width : null,
12938         height : null,
12939         preserveRatio : false,
12940         transparent: false,
12941         minX: 0,
12942         minY: 0,
12943         draggable: false,
12944
12945         /**
12946          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
12947          */
12948         constrainTo: undefined,
12949         /**
12950          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
12951          */
12952         resizeRegion: undefined,
12953
12954
12955     /**
12956      * Perform a manual resize
12957      * @param {Number} width
12958      * @param {Number} height
12959      */
12960     resizeTo : function(width, height){
12961         this.el.setSize(width, height);
12962         this.updateChildSize();
12963         this.fireEvent("resize", this, width, height, null);
12964     },
12965
12966     // private
12967     startSizing : function(e, handle){
12968         this.fireEvent("beforeresize", this, e);
12969         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
12970
12971             if(!this.overlay){
12972                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
12973                 this.overlay.unselectable();
12974                 this.overlay.enableDisplayMode("block");
12975                 this.overlay.on("mousemove", this.onMouseMove, this);
12976                 this.overlay.on("mouseup", this.onMouseUp, this);
12977             }
12978             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
12979
12980             this.resizing = true;
12981             this.startBox = this.el.getBox();
12982             this.startPoint = e.getXY();
12983             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
12984                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
12985
12986             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
12987             this.overlay.show();
12988
12989             if(this.constrainTo) {
12990                 var ct = Roo.get(this.constrainTo);
12991                 this.resizeRegion = ct.getRegion().adjust(
12992                     ct.getFrameWidth('t'),
12993                     ct.getFrameWidth('l'),
12994                     -ct.getFrameWidth('b'),
12995                     -ct.getFrameWidth('r')
12996                 );
12997             }
12998
12999             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13000             this.proxy.show();
13001             this.proxy.setBox(this.startBox);
13002             if(!this.dynamic){
13003                 this.proxy.setStyle('visibility', 'visible');
13004             }
13005         }
13006     },
13007
13008     // private
13009     onMouseDown : function(handle, e){
13010         if(this.enabled){
13011             e.stopEvent();
13012             this.activeHandle = handle;
13013             this.startSizing(e, handle);
13014         }
13015     },
13016
13017     // private
13018     onMouseUp : function(e){
13019         var size = this.resizeElement();
13020         this.resizing = false;
13021         this.handleOut();
13022         this.overlay.hide();
13023         this.proxy.hide();
13024         this.fireEvent("resize", this, size.width, size.height, e);
13025     },
13026
13027     // private
13028     updateChildSize : function(){
13029         
13030         if(this.resizeChild){
13031             var el = this.el;
13032             var child = this.resizeChild;
13033             var adj = this.adjustments;
13034             if(el.dom.offsetWidth){
13035                 var b = el.getSize(true);
13036                 child.setSize(b.width+adj[0], b.height+adj[1]);
13037             }
13038             // Second call here for IE
13039             // The first call enables instant resizing and
13040             // the second call corrects scroll bars if they
13041             // exist
13042             if(Roo.isIE){
13043                 setTimeout(function(){
13044                     if(el.dom.offsetWidth){
13045                         var b = el.getSize(true);
13046                         child.setSize(b.width+adj[0], b.height+adj[1]);
13047                     }
13048                 }, 10);
13049             }
13050         }
13051     },
13052
13053     // private
13054     snap : function(value, inc, min){
13055         if(!inc || !value) {
13056             return value;
13057         }
13058         var newValue = value;
13059         var m = value % inc;
13060         if(m > 0){
13061             if(m > (inc/2)){
13062                 newValue = value + (inc-m);
13063             }else{
13064                 newValue = value - m;
13065             }
13066         }
13067         return Math.max(min, newValue);
13068     },
13069
13070     // private
13071     resizeElement : function(){
13072         var box = this.proxy.getBox();
13073         if(this.updateBox){
13074             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13075         }else{
13076             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13077         }
13078         this.updateChildSize();
13079         if(!this.dynamic){
13080             this.proxy.hide();
13081         }
13082         return box;
13083     },
13084
13085     // private
13086     constrain : function(v, diff, m, mx){
13087         if(v - diff < m){
13088             diff = v - m;
13089         }else if(v - diff > mx){
13090             diff = mx - v;
13091         }
13092         return diff;
13093     },
13094
13095     // private
13096     onMouseMove : function(e){
13097         
13098         if(this.enabled){
13099             try{// try catch so if something goes wrong the user doesn't get hung
13100
13101             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13102                 return;
13103             }
13104
13105             //var curXY = this.startPoint;
13106             var curSize = this.curSize || this.startBox;
13107             var x = this.startBox.x, y = this.startBox.y;
13108             var ox = x, oy = y;
13109             var w = curSize.width, h = curSize.height;
13110             var ow = w, oh = h;
13111             var mw = this.minWidth, mh = this.minHeight;
13112             var mxw = this.maxWidth, mxh = this.maxHeight;
13113             var wi = this.widthIncrement;
13114             var hi = this.heightIncrement;
13115
13116             var eventXY = e.getXY();
13117             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13118             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13119
13120             var pos = this.activeHandle.position;
13121
13122             switch(pos){
13123                 case "east":
13124                     w += diffX;
13125                     w = Math.min(Math.max(mw, w), mxw);
13126                     break;
13127              
13128                 case "south":
13129                     h += diffY;
13130                     h = Math.min(Math.max(mh, h), mxh);
13131                     break;
13132                 case "southeast":
13133                     w += diffX;
13134                     h += diffY;
13135                     w = Math.min(Math.max(mw, w), mxw);
13136                     h = Math.min(Math.max(mh, h), mxh);
13137                     break;
13138                 case "north":
13139                     diffY = this.constrain(h, diffY, mh, mxh);
13140                     y += diffY;
13141                     h -= diffY;
13142                     break;
13143                 case "hdrag":
13144                     
13145                     if (wi) {
13146                         var adiffX = Math.abs(diffX);
13147                         var sub = (adiffX % wi); // how much 
13148                         if (sub > (wi/2)) { // far enough to snap
13149                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13150                         } else {
13151                             // remove difference.. 
13152                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13153                         }
13154                     }
13155                     x += diffX;
13156                     x = Math.max(this.minX, x);
13157                     break;
13158                 case "west":
13159                     diffX = this.constrain(w, diffX, mw, mxw);
13160                     x += diffX;
13161                     w -= diffX;
13162                     break;
13163                 case "northeast":
13164                     w += diffX;
13165                     w = Math.min(Math.max(mw, w), mxw);
13166                     diffY = this.constrain(h, diffY, mh, mxh);
13167                     y += diffY;
13168                     h -= diffY;
13169                     break;
13170                 case "northwest":
13171                     diffX = this.constrain(w, diffX, mw, mxw);
13172                     diffY = this.constrain(h, diffY, mh, mxh);
13173                     y += diffY;
13174                     h -= diffY;
13175                     x += diffX;
13176                     w -= diffX;
13177                     break;
13178                case "southwest":
13179                     diffX = this.constrain(w, diffX, mw, mxw);
13180                     h += diffY;
13181                     h = Math.min(Math.max(mh, h), mxh);
13182                     x += diffX;
13183                     w -= diffX;
13184                     break;
13185             }
13186
13187             var sw = this.snap(w, wi, mw);
13188             var sh = this.snap(h, hi, mh);
13189             if(sw != w || sh != h){
13190                 switch(pos){
13191                     case "northeast":
13192                         y -= sh - h;
13193                     break;
13194                     case "north":
13195                         y -= sh - h;
13196                         break;
13197                     case "southwest":
13198                         x -= sw - w;
13199                     break;
13200                     case "west":
13201                         x -= sw - w;
13202                         break;
13203                     case "northwest":
13204                         x -= sw - w;
13205                         y -= sh - h;
13206                     break;
13207                 }
13208                 w = sw;
13209                 h = sh;
13210             }
13211
13212             if(this.preserveRatio){
13213                 switch(pos){
13214                     case "southeast":
13215                     case "east":
13216                         h = oh * (w/ow);
13217                         h = Math.min(Math.max(mh, h), mxh);
13218                         w = ow * (h/oh);
13219                        break;
13220                     case "south":
13221                         w = ow * (h/oh);
13222                         w = Math.min(Math.max(mw, w), mxw);
13223                         h = oh * (w/ow);
13224                         break;
13225                     case "northeast":
13226                         w = ow * (h/oh);
13227                         w = Math.min(Math.max(mw, w), mxw);
13228                         h = oh * (w/ow);
13229                     break;
13230                     case "north":
13231                         var tw = w;
13232                         w = ow * (h/oh);
13233                         w = Math.min(Math.max(mw, w), mxw);
13234                         h = oh * (w/ow);
13235                         x += (tw - w) / 2;
13236                         break;
13237                     case "southwest":
13238                         h = oh * (w/ow);
13239                         h = Math.min(Math.max(mh, h), mxh);
13240                         var tw = w;
13241                         w = ow * (h/oh);
13242                         x += tw - w;
13243                         break;
13244                     case "west":
13245                         var th = h;
13246                         h = oh * (w/ow);
13247                         h = Math.min(Math.max(mh, h), mxh);
13248                         y += (th - h) / 2;
13249                         var tw = w;
13250                         w = ow * (h/oh);
13251                         x += tw - w;
13252                        break;
13253                     case "northwest":
13254                         var tw = w;
13255                         var th = h;
13256                         h = oh * (w/ow);
13257                         h = Math.min(Math.max(mh, h), mxh);
13258                         w = ow * (h/oh);
13259                         y += th - h;
13260                         x += tw - w;
13261                        break;
13262
13263                 }
13264             }
13265             if (pos == 'hdrag') {
13266                 w = ow;
13267             }
13268             this.proxy.setBounds(x, y, w, h);
13269             if(this.dynamic){
13270                 this.resizeElement();
13271             }
13272             }catch(e){}
13273         }
13274         this.fireEvent("resizing", this, x, y, w, h, e);
13275     },
13276
13277     // private
13278     handleOver : function(){
13279         if(this.enabled){
13280             this.el.addClass("x-resizable-over");
13281         }
13282     },
13283
13284     // private
13285     handleOut : function(){
13286         if(!this.resizing){
13287             this.el.removeClass("x-resizable-over");
13288         }
13289     },
13290
13291     /**
13292      * Returns the element this component is bound to.
13293      * @return {Roo.Element}
13294      */
13295     getEl : function(){
13296         return this.el;
13297     },
13298
13299     /**
13300      * Returns the resizeChild element (or null).
13301      * @return {Roo.Element}
13302      */
13303     getResizeChild : function(){
13304         return this.resizeChild;
13305     },
13306     groupHandler : function()
13307     {
13308         
13309     },
13310     /**
13311      * Destroys this resizable. If the element was wrapped and
13312      * removeEl is not true then the element remains.
13313      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13314      */
13315     destroy : function(removeEl){
13316         this.proxy.remove();
13317         if(this.overlay){
13318             this.overlay.removeAllListeners();
13319             this.overlay.remove();
13320         }
13321         var ps = Roo.Resizable.positions;
13322         for(var k in ps){
13323             if(typeof ps[k] != "function" && this[ps[k]]){
13324                 var h = this[ps[k]];
13325                 h.el.removeAllListeners();
13326                 h.el.remove();
13327             }
13328         }
13329         if(removeEl){
13330             this.el.update("");
13331             this.el.remove();
13332         }
13333     }
13334 });
13335
13336 // private
13337 // hash to map config positions to true positions
13338 Roo.Resizable.positions = {
13339     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13340     hd: "hdrag"
13341 };
13342
13343 // private
13344 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13345     if(!this.tpl){
13346         // only initialize the template if resizable is used
13347         var tpl = Roo.DomHelper.createTemplate(
13348             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13349         );
13350         tpl.compile();
13351         Roo.Resizable.Handle.prototype.tpl = tpl;
13352     }
13353     this.position = pos;
13354     this.rz = rz;
13355     // show north drag fro topdra
13356     var handlepos = pos == 'hdrag' ? 'north' : pos;
13357     
13358     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13359     if (pos == 'hdrag') {
13360         this.el.setStyle('cursor', 'pointer');
13361     }
13362     this.el.unselectable();
13363     if(transparent){
13364         this.el.setOpacity(0);
13365     }
13366     this.el.on("mousedown", this.onMouseDown, this);
13367     if(!disableTrackOver){
13368         this.el.on("mouseover", this.onMouseOver, this);
13369         this.el.on("mouseout", this.onMouseOut, this);
13370     }
13371 };
13372
13373 // private
13374 Roo.Resizable.Handle.prototype = {
13375     afterResize : function(rz){
13376         Roo.log('after?');
13377         // do nothing
13378     },
13379     // private
13380     onMouseDown : function(e){
13381         this.rz.onMouseDown(this, e);
13382     },
13383     // private
13384     onMouseOver : function(e){
13385         this.rz.handleOver(this, e);
13386     },
13387     // private
13388     onMouseOut : function(e){
13389         this.rz.handleOut(this, e);
13390     }
13391 };/*
13392  * Based on:
13393  * Ext JS Library 1.1.1
13394  * Copyright(c) 2006-2007, Ext JS, LLC.
13395  *
13396  * Originally Released Under LGPL - original licence link has changed is not relivant.
13397  *
13398  * Fork - LGPL
13399  * <script type="text/javascript">
13400  */
13401
13402 /**
13403  * @class Roo.Editor
13404  * @extends Roo.Component
13405  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13406  * @constructor
13407  * Create a new Editor
13408  * @param {Roo.form.Field} field The Field object (or descendant)
13409  * @param {Object} config The config object
13410  */
13411 Roo.Editor = function(field, config){
13412     Roo.Editor.superclass.constructor.call(this, config);
13413     this.field = field;
13414     this.addEvents({
13415         /**
13416              * @event beforestartedit
13417              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13418              * false from the handler of this event.
13419              * @param {Editor} this
13420              * @param {Roo.Element} boundEl The underlying element bound to this editor
13421              * @param {Mixed} value The field value being set
13422              */
13423         "beforestartedit" : true,
13424         /**
13425              * @event startedit
13426              * Fires when this editor is displayed
13427              * @param {Roo.Element} boundEl The underlying element bound to this editor
13428              * @param {Mixed} value The starting field value
13429              */
13430         "startedit" : true,
13431         /**
13432              * @event beforecomplete
13433              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13434              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13435              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13436              * event will not fire since no edit actually occurred.
13437              * @param {Editor} this
13438              * @param {Mixed} value The current field value
13439              * @param {Mixed} startValue The original field value
13440              */
13441         "beforecomplete" : true,
13442         /**
13443              * @event complete
13444              * Fires after editing is complete and any changed value has been written to the underlying field.
13445              * @param {Editor} this
13446              * @param {Mixed} value The current field value
13447              * @param {Mixed} startValue The original field value
13448              */
13449         "complete" : true,
13450         /**
13451          * @event specialkey
13452          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13453          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13454          * @param {Roo.form.Field} this
13455          * @param {Roo.EventObject} e The event object
13456          */
13457         "specialkey" : true
13458     });
13459 };
13460
13461 Roo.extend(Roo.Editor, Roo.Component, {
13462     /**
13463      * @cfg {Boolean/String} autosize
13464      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13465      * or "height" to adopt the height only (defaults to false)
13466      */
13467     /**
13468      * @cfg {Boolean} revertInvalid
13469      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13470      * validation fails (defaults to true)
13471      */
13472     /**
13473      * @cfg {Boolean} ignoreNoChange
13474      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13475      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13476      * will never be ignored.
13477      */
13478     /**
13479      * @cfg {Boolean} hideEl
13480      * False to keep the bound element visible while the editor is displayed (defaults to true)
13481      */
13482     /**
13483      * @cfg {Mixed} value
13484      * The data value of the underlying field (defaults to "")
13485      */
13486     value : "",
13487     /**
13488      * @cfg {String} alignment
13489      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13490      */
13491     alignment: "c-c?",
13492     /**
13493      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13494      * for bottom-right shadow (defaults to "frame")
13495      */
13496     shadow : "frame",
13497     /**
13498      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13499      */
13500     constrain : false,
13501     /**
13502      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13503      */
13504     completeOnEnter : false,
13505     /**
13506      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13507      */
13508     cancelOnEsc : false,
13509     /**
13510      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13511      */
13512     updateEl : false,
13513
13514     // private
13515     onRender : function(ct, position){
13516         this.el = new Roo.Layer({
13517             shadow: this.shadow,
13518             cls: "x-editor",
13519             parentEl : ct,
13520             shim : this.shim,
13521             shadowOffset:4,
13522             id: this.id,
13523             constrain: this.constrain
13524         });
13525         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13526         if(this.field.msgTarget != 'title'){
13527             this.field.msgTarget = 'qtip';
13528         }
13529         this.field.render(this.el);
13530         if(Roo.isGecko){
13531             this.field.el.dom.setAttribute('autocomplete', 'off');
13532         }
13533         this.field.on("specialkey", this.onSpecialKey, this);
13534         if(this.swallowKeys){
13535             this.field.el.swallowEvent(['keydown','keypress']);
13536         }
13537         this.field.show();
13538         this.field.on("blur", this.onBlur, this);
13539         if(this.field.grow){
13540             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13541         }
13542     },
13543
13544     onSpecialKey : function(field, e)
13545     {
13546         //Roo.log('editor onSpecialKey');
13547         if(this.completeOnEnter && e.getKey() == e.ENTER){
13548             e.stopEvent();
13549             this.completeEdit();
13550             return;
13551         }
13552         // do not fire special key otherwise it might hide close the editor...
13553         if(e.getKey() == e.ENTER){    
13554             return;
13555         }
13556         if(this.cancelOnEsc && e.getKey() == e.ESC){
13557             this.cancelEdit();
13558             return;
13559         } 
13560         this.fireEvent('specialkey', field, e);
13561     
13562     },
13563
13564     /**
13565      * Starts the editing process and shows the editor.
13566      * @param {String/HTMLElement/Element} el The element to edit
13567      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13568       * to the innerHTML of el.
13569      */
13570     startEdit : function(el, value){
13571         if(this.editing){
13572             this.completeEdit();
13573         }
13574         this.boundEl = Roo.get(el);
13575         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13576         if(!this.rendered){
13577             this.render(this.parentEl || document.body);
13578         }
13579         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13580             return;
13581         }
13582         this.startValue = v;
13583         this.field.setValue(v);
13584         if(this.autoSize){
13585             var sz = this.boundEl.getSize();
13586             switch(this.autoSize){
13587                 case "width":
13588                 this.setSize(sz.width,  "");
13589                 break;
13590                 case "height":
13591                 this.setSize("",  sz.height);
13592                 break;
13593                 default:
13594                 this.setSize(sz.width,  sz.height);
13595             }
13596         }
13597         this.el.alignTo(this.boundEl, this.alignment);
13598         this.editing = true;
13599         if(Roo.QuickTips){
13600             Roo.QuickTips.disable();
13601         }
13602         this.show();
13603     },
13604
13605     /**
13606      * Sets the height and width of this editor.
13607      * @param {Number} width The new width
13608      * @param {Number} height The new height
13609      */
13610     setSize : function(w, h){
13611         this.field.setSize(w, h);
13612         if(this.el){
13613             this.el.sync();
13614         }
13615     },
13616
13617     /**
13618      * Realigns the editor to the bound field based on the current alignment config value.
13619      */
13620     realign : function(){
13621         this.el.alignTo(this.boundEl, this.alignment);
13622     },
13623
13624     /**
13625      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
13626      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
13627      */
13628     completeEdit : function(remainVisible){
13629         if(!this.editing){
13630             return;
13631         }
13632         var v = this.getValue();
13633         if(this.revertInvalid !== false && !this.field.isValid()){
13634             v = this.startValue;
13635             this.cancelEdit(true);
13636         }
13637         if(String(v) === String(this.startValue) && this.ignoreNoChange){
13638             this.editing = false;
13639             this.hide();
13640             return;
13641         }
13642         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
13643             this.editing = false;
13644             if(this.updateEl && this.boundEl){
13645                 this.boundEl.update(v);
13646             }
13647             if(remainVisible !== true){
13648                 this.hide();
13649             }
13650             this.fireEvent("complete", this, v, this.startValue);
13651         }
13652     },
13653
13654     // private
13655     onShow : function(){
13656         this.el.show();
13657         if(this.hideEl !== false){
13658             this.boundEl.hide();
13659         }
13660         this.field.show();
13661         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
13662             this.fixIEFocus = true;
13663             this.deferredFocus.defer(50, this);
13664         }else{
13665             this.field.focus();
13666         }
13667         this.fireEvent("startedit", this.boundEl, this.startValue);
13668     },
13669
13670     deferredFocus : function(){
13671         if(this.editing){
13672             this.field.focus();
13673         }
13674     },
13675
13676     /**
13677      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
13678      * reverted to the original starting value.
13679      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
13680      * cancel (defaults to false)
13681      */
13682     cancelEdit : function(remainVisible){
13683         if(this.editing){
13684             this.setValue(this.startValue);
13685             if(remainVisible !== true){
13686                 this.hide();
13687             }
13688         }
13689     },
13690
13691     // private
13692     onBlur : function(){
13693         if(this.allowBlur !== true && this.editing){
13694             this.completeEdit();
13695         }
13696     },
13697
13698     // private
13699     onHide : function(){
13700         if(this.editing){
13701             this.completeEdit();
13702             return;
13703         }
13704         this.field.blur();
13705         if(this.field.collapse){
13706             this.field.collapse();
13707         }
13708         this.el.hide();
13709         if(this.hideEl !== false){
13710             this.boundEl.show();
13711         }
13712         if(Roo.QuickTips){
13713             Roo.QuickTips.enable();
13714         }
13715     },
13716
13717     /**
13718      * Sets the data value of the editor
13719      * @param {Mixed} value Any valid value supported by the underlying field
13720      */
13721     setValue : function(v){
13722         this.field.setValue(v);
13723     },
13724
13725     /**
13726      * Gets the data value of the editor
13727      * @return {Mixed} The data value
13728      */
13729     getValue : function(){
13730         return this.field.getValue();
13731     }
13732 });/*
13733  * Based on:
13734  * Ext JS Library 1.1.1
13735  * Copyright(c) 2006-2007, Ext JS, LLC.
13736  *
13737  * Originally Released Under LGPL - original licence link has changed is not relivant.
13738  *
13739  * Fork - LGPL
13740  * <script type="text/javascript">
13741  */
13742  
13743 /**
13744  * @class Roo.BasicDialog
13745  * @extends Roo.util.Observable
13746  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
13747  * <pre><code>
13748 var dlg = new Roo.BasicDialog("my-dlg", {
13749     height: 200,
13750     width: 300,
13751     minHeight: 100,
13752     minWidth: 150,
13753     modal: true,
13754     proxyDrag: true,
13755     shadow: true
13756 });
13757 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
13758 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
13759 dlg.addButton('Cancel', dlg.hide, dlg);
13760 dlg.show();
13761 </code></pre>
13762   <b>A Dialog should always be a direct child of the body element.</b>
13763  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
13764  * @cfg {String} title Default text to display in the title bar (defaults to null)
13765  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
13766  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
13767  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
13768  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
13769  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
13770  * (defaults to null with no animation)
13771  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
13772  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
13773  * property for valid values (defaults to 'all')
13774  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
13775  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
13776  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
13777  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
13778  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
13779  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
13780  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
13781  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
13782  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
13783  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
13784  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
13785  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
13786  * draggable = true (defaults to false)
13787  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
13788  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13789  * shadow (defaults to false)
13790  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
13791  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
13792  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
13793  * @cfg {Array} buttons Array of buttons
13794  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
13795  * @constructor
13796  * Create a new BasicDialog.
13797  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
13798  * @param {Object} config Configuration options
13799  */
13800 Roo.BasicDialog = function(el, config){
13801     this.el = Roo.get(el);
13802     var dh = Roo.DomHelper;
13803     if(!this.el && config && config.autoCreate){
13804         if(typeof config.autoCreate == "object"){
13805             if(!config.autoCreate.id){
13806                 config.autoCreate.id = el;
13807             }
13808             this.el = dh.append(document.body,
13809                         config.autoCreate, true);
13810         }else{
13811             this.el = dh.append(document.body,
13812                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
13813         }
13814     }
13815     el = this.el;
13816     el.setDisplayed(true);
13817     el.hide = this.hideAction;
13818     this.id = el.id;
13819     el.addClass("x-dlg");
13820
13821     Roo.apply(this, config);
13822
13823     this.proxy = el.createProxy("x-dlg-proxy");
13824     this.proxy.hide = this.hideAction;
13825     this.proxy.setOpacity(.5);
13826     this.proxy.hide();
13827
13828     if(config.width){
13829         el.setWidth(config.width);
13830     }
13831     if(config.height){
13832         el.setHeight(config.height);
13833     }
13834     this.size = el.getSize();
13835     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
13836         this.xy = [config.x,config.y];
13837     }else{
13838         this.xy = el.getCenterXY(true);
13839     }
13840     /** The header element @type Roo.Element */
13841     this.header = el.child("> .x-dlg-hd");
13842     /** The body element @type Roo.Element */
13843     this.body = el.child("> .x-dlg-bd");
13844     /** The footer element @type Roo.Element */
13845     this.footer = el.child("> .x-dlg-ft");
13846
13847     if(!this.header){
13848         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
13849     }
13850     if(!this.body){
13851         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
13852     }
13853
13854     this.header.unselectable();
13855     if(this.title){
13856         this.header.update(this.title);
13857     }
13858     // this element allows the dialog to be focused for keyboard event
13859     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
13860     this.focusEl.swallowEvent("click", true);
13861
13862     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
13863
13864     // wrap the body and footer for special rendering
13865     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
13866     if(this.footer){
13867         this.bwrap.dom.appendChild(this.footer.dom);
13868     }
13869
13870     this.bg = this.el.createChild({
13871         tag: "div", cls:"x-dlg-bg",
13872         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
13873     });
13874     this.centerBg = this.bg.child("div.x-dlg-bg-center");
13875
13876
13877     if(this.autoScroll !== false && !this.autoTabs){
13878         this.body.setStyle("overflow", "auto");
13879     }
13880
13881     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
13882
13883     if(this.closable !== false){
13884         this.el.addClass("x-dlg-closable");
13885         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
13886         this.close.on("click", this.closeClick, this);
13887         this.close.addClassOnOver("x-dlg-close-over");
13888     }
13889     if(this.collapsible !== false){
13890         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
13891         this.collapseBtn.on("click", this.collapseClick, this);
13892         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
13893         this.header.on("dblclick", this.collapseClick, this);
13894     }
13895     if(this.resizable !== false){
13896         this.el.addClass("x-dlg-resizable");
13897         this.resizer = new Roo.Resizable(el, {
13898             minWidth: this.minWidth || 80,
13899             minHeight:this.minHeight || 80,
13900             handles: this.resizeHandles || "all",
13901             pinned: true
13902         });
13903         this.resizer.on("beforeresize", this.beforeResize, this);
13904         this.resizer.on("resize", this.onResize, this);
13905     }
13906     if(this.draggable !== false){
13907         el.addClass("x-dlg-draggable");
13908         if (!this.proxyDrag) {
13909             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
13910         }
13911         else {
13912             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
13913         }
13914         dd.setHandleElId(this.header.id);
13915         dd.endDrag = this.endMove.createDelegate(this);
13916         dd.startDrag = this.startMove.createDelegate(this);
13917         dd.onDrag = this.onDrag.createDelegate(this);
13918         dd.scroll = false;
13919         this.dd = dd;
13920     }
13921     if(this.modal){
13922         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
13923         this.mask.enableDisplayMode("block");
13924         this.mask.hide();
13925         this.el.addClass("x-dlg-modal");
13926     }
13927     if(this.shadow){
13928         this.shadow = new Roo.Shadow({
13929             mode : typeof this.shadow == "string" ? this.shadow : "sides",
13930             offset : this.shadowOffset
13931         });
13932     }else{
13933         this.shadowOffset = 0;
13934     }
13935     if(Roo.useShims && this.shim !== false){
13936         this.shim = this.el.createShim();
13937         this.shim.hide = this.hideAction;
13938         this.shim.hide();
13939     }else{
13940         this.shim = false;
13941     }
13942     if(this.autoTabs){
13943         this.initTabs();
13944     }
13945     if (this.buttons) { 
13946         var bts= this.buttons;
13947         this.buttons = [];
13948         Roo.each(bts, function(b) {
13949             this.addButton(b);
13950         }, this);
13951     }
13952     
13953     
13954     this.addEvents({
13955         /**
13956          * @event keydown
13957          * Fires when a key is pressed
13958          * @param {Roo.BasicDialog} this
13959          * @param {Roo.EventObject} e
13960          */
13961         "keydown" : true,
13962         /**
13963          * @event move
13964          * Fires when this dialog is moved by the user.
13965          * @param {Roo.BasicDialog} this
13966          * @param {Number} x The new page X
13967          * @param {Number} y The new page Y
13968          */
13969         "move" : true,
13970         /**
13971          * @event resize
13972          * Fires when this dialog is resized by the user.
13973          * @param {Roo.BasicDialog} this
13974          * @param {Number} width The new width
13975          * @param {Number} height The new height
13976          */
13977         "resize" : true,
13978         /**
13979          * @event beforehide
13980          * Fires before this dialog is hidden.
13981          * @param {Roo.BasicDialog} this
13982          */
13983         "beforehide" : true,
13984         /**
13985          * @event hide
13986          * Fires when this dialog is hidden.
13987          * @param {Roo.BasicDialog} this
13988          */
13989         "hide" : true,
13990         /**
13991          * @event beforeshow
13992          * Fires before this dialog is shown.
13993          * @param {Roo.BasicDialog} this
13994          */
13995         "beforeshow" : true,
13996         /**
13997          * @event show
13998          * Fires when this dialog is shown.
13999          * @param {Roo.BasicDialog} this
14000          */
14001         "show" : true
14002     });
14003     el.on("keydown", this.onKeyDown, this);
14004     el.on("mousedown", this.toFront, this);
14005     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14006     this.el.hide();
14007     Roo.DialogManager.register(this);
14008     Roo.BasicDialog.superclass.constructor.call(this);
14009 };
14010
14011 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14012     shadowOffset: Roo.isIE ? 6 : 5,
14013     minHeight: 80,
14014     minWidth: 200,
14015     minButtonWidth: 75,
14016     defaultButton: null,
14017     buttonAlign: "right",
14018     tabTag: 'div',
14019     firstShow: true,
14020
14021     /**
14022      * Sets the dialog title text
14023      * @param {String} text The title text to display
14024      * @return {Roo.BasicDialog} this
14025      */
14026     setTitle : function(text){
14027         this.header.update(text);
14028         return this;
14029     },
14030
14031     // private
14032     closeClick : function(){
14033         this.hide();
14034     },
14035
14036     // private
14037     collapseClick : function(){
14038         this[this.collapsed ? "expand" : "collapse"]();
14039     },
14040
14041     /**
14042      * Collapses the dialog to its minimized state (only the title bar is visible).
14043      * Equivalent to the user clicking the collapse dialog button.
14044      */
14045     collapse : function(){
14046         if(!this.collapsed){
14047             this.collapsed = true;
14048             this.el.addClass("x-dlg-collapsed");
14049             this.restoreHeight = this.el.getHeight();
14050             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14051         }
14052     },
14053
14054     /**
14055      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14056      * clicking the expand dialog button.
14057      */
14058     expand : function(){
14059         if(this.collapsed){
14060             this.collapsed = false;
14061             this.el.removeClass("x-dlg-collapsed");
14062             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14063         }
14064     },
14065
14066     /**
14067      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14068      * @return {Roo.TabPanel} The tabs component
14069      */
14070     initTabs : function(){
14071         var tabs = this.getTabs();
14072         while(tabs.getTab(0)){
14073             tabs.removeTab(0);
14074         }
14075         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14076             var dom = el.dom;
14077             tabs.addTab(Roo.id(dom), dom.title);
14078             dom.title = "";
14079         });
14080         tabs.activate(0);
14081         return tabs;
14082     },
14083
14084     // private
14085     beforeResize : function(){
14086         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14087     },
14088
14089     // private
14090     onResize : function(){
14091         this.refreshSize();
14092         this.syncBodyHeight();
14093         this.adjustAssets();
14094         this.focus();
14095         this.fireEvent("resize", this, this.size.width, this.size.height);
14096     },
14097
14098     // private
14099     onKeyDown : function(e){
14100         if(this.isVisible()){
14101             this.fireEvent("keydown", this, e);
14102         }
14103     },
14104
14105     /**
14106      * Resizes the dialog.
14107      * @param {Number} width
14108      * @param {Number} height
14109      * @return {Roo.BasicDialog} this
14110      */
14111     resizeTo : function(width, height){
14112         this.el.setSize(width, height);
14113         this.size = {width: width, height: height};
14114         this.syncBodyHeight();
14115         if(this.fixedcenter){
14116             this.center();
14117         }
14118         if(this.isVisible()){
14119             this.constrainXY();
14120             this.adjustAssets();
14121         }
14122         this.fireEvent("resize", this, width, height);
14123         return this;
14124     },
14125
14126
14127     /**
14128      * Resizes the dialog to fit the specified content size.
14129      * @param {Number} width
14130      * @param {Number} height
14131      * @return {Roo.BasicDialog} this
14132      */
14133     setContentSize : function(w, h){
14134         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14135         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14136         //if(!this.el.isBorderBox()){
14137             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14138             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14139         //}
14140         if(this.tabs){
14141             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14142             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14143         }
14144         this.resizeTo(w, h);
14145         return this;
14146     },
14147
14148     /**
14149      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14150      * executed in response to a particular key being pressed while the dialog is active.
14151      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14152      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14153      * @param {Function} fn The function to call
14154      * @param {Object} scope (optional) The scope of the function
14155      * @return {Roo.BasicDialog} this
14156      */
14157     addKeyListener : function(key, fn, scope){
14158         var keyCode, shift, ctrl, alt;
14159         if(typeof key == "object" && !(key instanceof Array)){
14160             keyCode = key["key"];
14161             shift = key["shift"];
14162             ctrl = key["ctrl"];
14163             alt = key["alt"];
14164         }else{
14165             keyCode = key;
14166         }
14167         var handler = function(dlg, e){
14168             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14169                 var k = e.getKey();
14170                 if(keyCode instanceof Array){
14171                     for(var i = 0, len = keyCode.length; i < len; i++){
14172                         if(keyCode[i] == k){
14173                           fn.call(scope || window, dlg, k, e);
14174                           return;
14175                         }
14176                     }
14177                 }else{
14178                     if(k == keyCode){
14179                         fn.call(scope || window, dlg, k, e);
14180                     }
14181                 }
14182             }
14183         };
14184         this.on("keydown", handler);
14185         return this;
14186     },
14187
14188     /**
14189      * Returns the TabPanel component (creates it if it doesn't exist).
14190      * Note: If you wish to simply check for the existence of tabs without creating them,
14191      * check for a null 'tabs' property.
14192      * @return {Roo.TabPanel} The tabs component
14193      */
14194     getTabs : function(){
14195         if(!this.tabs){
14196             this.el.addClass("x-dlg-auto-tabs");
14197             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14198             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14199         }
14200         return this.tabs;
14201     },
14202
14203     /**
14204      * Adds a button to the footer section of the dialog.
14205      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14206      * object or a valid Roo.DomHelper element config
14207      * @param {Function} handler The function called when the button is clicked
14208      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14209      * @return {Roo.Button} The new button
14210      */
14211     addButton : function(config, handler, scope){
14212         var dh = Roo.DomHelper;
14213         if(!this.footer){
14214             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14215         }
14216         if(!this.btnContainer){
14217             var tb = this.footer.createChild({
14218
14219                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14220                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14221             }, null, true);
14222             this.btnContainer = tb.firstChild.firstChild.firstChild;
14223         }
14224         var bconfig = {
14225             handler: handler,
14226             scope: scope,
14227             minWidth: this.minButtonWidth,
14228             hideParent:true
14229         };
14230         if(typeof config == "string"){
14231             bconfig.text = config;
14232         }else{
14233             if(config.tag){
14234                 bconfig.dhconfig = config;
14235             }else{
14236                 Roo.apply(bconfig, config);
14237             }
14238         }
14239         var fc = false;
14240         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14241             bconfig.position = Math.max(0, bconfig.position);
14242             fc = this.btnContainer.childNodes[bconfig.position];
14243         }
14244          
14245         var btn = new Roo.Button(
14246             fc ? 
14247                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14248                 : this.btnContainer.appendChild(document.createElement("td")),
14249             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14250             bconfig
14251         );
14252         this.syncBodyHeight();
14253         if(!this.buttons){
14254             /**
14255              * Array of all the buttons that have been added to this dialog via addButton
14256              * @type Array
14257              */
14258             this.buttons = [];
14259         }
14260         this.buttons.push(btn);
14261         return btn;
14262     },
14263
14264     /**
14265      * Sets the default button to be focused when the dialog is displayed.
14266      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14267      * @return {Roo.BasicDialog} this
14268      */
14269     setDefaultButton : function(btn){
14270         this.defaultButton = btn;
14271         return this;
14272     },
14273
14274     // private
14275     getHeaderFooterHeight : function(safe){
14276         var height = 0;
14277         if(this.header){
14278            height += this.header.getHeight();
14279         }
14280         if(this.footer){
14281            var fm = this.footer.getMargins();
14282             height += (this.footer.getHeight()+fm.top+fm.bottom);
14283         }
14284         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14285         height += this.centerBg.getPadding("tb");
14286         return height;
14287     },
14288
14289     // private
14290     syncBodyHeight : function()
14291     {
14292         var bd = this.body, // the text
14293             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
14294             bw = this.bwrap;
14295         var height = this.size.height - this.getHeaderFooterHeight(false);
14296         bd.setHeight(height-bd.getMargins("tb"));
14297         var hh = this.header.getHeight();
14298         var h = this.size.height-hh;
14299         cb.setHeight(h);
14300         
14301         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14302         bw.setHeight(h-cb.getPadding("tb"));
14303         
14304         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14305         bd.setWidth(bw.getWidth(true));
14306         if(this.tabs){
14307             this.tabs.syncHeight();
14308             if(Roo.isIE){
14309                 this.tabs.el.repaint();
14310             }
14311         }
14312     },
14313
14314     /**
14315      * Restores the previous state of the dialog if Roo.state is configured.
14316      * @return {Roo.BasicDialog} this
14317      */
14318     restoreState : function(){
14319         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14320         if(box && box.width){
14321             this.xy = [box.x, box.y];
14322             this.resizeTo(box.width, box.height);
14323         }
14324         return this;
14325     },
14326
14327     // private
14328     beforeShow : function(){
14329         this.expand();
14330         if(this.fixedcenter){
14331             this.xy = this.el.getCenterXY(true);
14332         }
14333         if(this.modal){
14334             Roo.get(document.body).addClass("x-body-masked");
14335             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14336             this.mask.show();
14337         }
14338         this.constrainXY();
14339     },
14340
14341     // private
14342     animShow : function(){
14343         var b = Roo.get(this.animateTarget).getBox();
14344         this.proxy.setSize(b.width, b.height);
14345         this.proxy.setLocation(b.x, b.y);
14346         this.proxy.show();
14347         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14348                     true, .35, this.showEl.createDelegate(this));
14349     },
14350
14351     /**
14352      * Shows the dialog.
14353      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14354      * @return {Roo.BasicDialog} this
14355      */
14356     show : function(animateTarget){
14357         if (this.fireEvent("beforeshow", this) === false){
14358             return;
14359         }
14360         if(this.syncHeightBeforeShow){
14361             this.syncBodyHeight();
14362         }else if(this.firstShow){
14363             this.firstShow = false;
14364             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14365         }
14366         this.animateTarget = animateTarget || this.animateTarget;
14367         if(!this.el.isVisible()){
14368             this.beforeShow();
14369             if(this.animateTarget && Roo.get(this.animateTarget)){
14370                 this.animShow();
14371             }else{
14372                 this.showEl();
14373             }
14374         }
14375         return this;
14376     },
14377
14378     // private
14379     showEl : function(){
14380         this.proxy.hide();
14381         this.el.setXY(this.xy);
14382         this.el.show();
14383         this.adjustAssets(true);
14384         this.toFront();
14385         this.focus();
14386         // IE peekaboo bug - fix found by Dave Fenwick
14387         if(Roo.isIE){
14388             this.el.repaint();
14389         }
14390         this.fireEvent("show", this);
14391     },
14392
14393     /**
14394      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14395      * dialog itself will receive focus.
14396      */
14397     focus : function(){
14398         if(this.defaultButton){
14399             this.defaultButton.focus();
14400         }else{
14401             this.focusEl.focus();
14402         }
14403     },
14404
14405     // private
14406     constrainXY : function(){
14407         if(this.constraintoviewport !== false){
14408             if(!this.viewSize){
14409                 if(this.container){
14410                     var s = this.container.getSize();
14411                     this.viewSize = [s.width, s.height];
14412                 }else{
14413                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14414                 }
14415             }
14416             var s = Roo.get(this.container||document).getScroll();
14417
14418             var x = this.xy[0], y = this.xy[1];
14419             var w = this.size.width, h = this.size.height;
14420             var vw = this.viewSize[0], vh = this.viewSize[1];
14421             // only move it if it needs it
14422             var moved = false;
14423             // first validate right/bottom
14424             if(x + w > vw+s.left){
14425                 x = vw - w;
14426                 moved = true;
14427             }
14428             if(y + h > vh+s.top){
14429                 y = vh - h;
14430                 moved = true;
14431             }
14432             // then make sure top/left isn't negative
14433             if(x < s.left){
14434                 x = s.left;
14435                 moved = true;
14436             }
14437             if(y < s.top){
14438                 y = s.top;
14439                 moved = true;
14440             }
14441             if(moved){
14442                 // cache xy
14443                 this.xy = [x, y];
14444                 if(this.isVisible()){
14445                     this.el.setLocation(x, y);
14446                     this.adjustAssets();
14447                 }
14448             }
14449         }
14450     },
14451
14452     // private
14453     onDrag : function(){
14454         if(!this.proxyDrag){
14455             this.xy = this.el.getXY();
14456             this.adjustAssets();
14457         }
14458     },
14459
14460     // private
14461     adjustAssets : function(doShow){
14462         var x = this.xy[0], y = this.xy[1];
14463         var w = this.size.width, h = this.size.height;
14464         if(doShow === true){
14465             if(this.shadow){
14466                 this.shadow.show(this.el);
14467             }
14468             if(this.shim){
14469                 this.shim.show();
14470             }
14471         }
14472         if(this.shadow && this.shadow.isVisible()){
14473             this.shadow.show(this.el);
14474         }
14475         if(this.shim && this.shim.isVisible()){
14476             this.shim.setBounds(x, y, w, h);
14477         }
14478     },
14479
14480     // private
14481     adjustViewport : function(w, h){
14482         if(!w || !h){
14483             w = Roo.lib.Dom.getViewWidth();
14484             h = Roo.lib.Dom.getViewHeight();
14485         }
14486         // cache the size
14487         this.viewSize = [w, h];
14488         if(this.modal && this.mask.isVisible()){
14489             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14490             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14491         }
14492         if(this.isVisible()){
14493             this.constrainXY();
14494         }
14495     },
14496
14497     /**
14498      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14499      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14500      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14501      */
14502     destroy : function(removeEl){
14503         if(this.isVisible()){
14504             this.animateTarget = null;
14505             this.hide();
14506         }
14507         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14508         if(this.tabs){
14509             this.tabs.destroy(removeEl);
14510         }
14511         Roo.destroy(
14512              this.shim,
14513              this.proxy,
14514              this.resizer,
14515              this.close,
14516              this.mask
14517         );
14518         if(this.dd){
14519             this.dd.unreg();
14520         }
14521         if(this.buttons){
14522            for(var i = 0, len = this.buttons.length; i < len; i++){
14523                this.buttons[i].destroy();
14524            }
14525         }
14526         this.el.removeAllListeners();
14527         if(removeEl === true){
14528             this.el.update("");
14529             this.el.remove();
14530         }
14531         Roo.DialogManager.unregister(this);
14532     },
14533
14534     // private
14535     startMove : function(){
14536         if(this.proxyDrag){
14537             this.proxy.show();
14538         }
14539         if(this.constraintoviewport !== false){
14540             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14541         }
14542     },
14543
14544     // private
14545     endMove : function(){
14546         if(!this.proxyDrag){
14547             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14548         }else{
14549             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14550             this.proxy.hide();
14551         }
14552         this.refreshSize();
14553         this.adjustAssets();
14554         this.focus();
14555         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14556     },
14557
14558     /**
14559      * Brings this dialog to the front of any other visible dialogs
14560      * @return {Roo.BasicDialog} this
14561      */
14562     toFront : function(){
14563         Roo.DialogManager.bringToFront(this);
14564         return this;
14565     },
14566
14567     /**
14568      * Sends this dialog to the back (under) of any other visible dialogs
14569      * @return {Roo.BasicDialog} this
14570      */
14571     toBack : function(){
14572         Roo.DialogManager.sendToBack(this);
14573         return this;
14574     },
14575
14576     /**
14577      * Centers this dialog in the viewport
14578      * @return {Roo.BasicDialog} this
14579      */
14580     center : function(){
14581         var xy = this.el.getCenterXY(true);
14582         this.moveTo(xy[0], xy[1]);
14583         return this;
14584     },
14585
14586     /**
14587      * Moves the dialog's top-left corner to the specified point
14588      * @param {Number} x
14589      * @param {Number} y
14590      * @return {Roo.BasicDialog} this
14591      */
14592     moveTo : function(x, y){
14593         this.xy = [x,y];
14594         if(this.isVisible()){
14595             this.el.setXY(this.xy);
14596             this.adjustAssets();
14597         }
14598         return this;
14599     },
14600
14601     /**
14602      * Aligns the dialog to the specified element
14603      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14604      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
14605      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14606      * @return {Roo.BasicDialog} this
14607      */
14608     alignTo : function(element, position, offsets){
14609         this.xy = this.el.getAlignToXY(element, position, offsets);
14610         if(this.isVisible()){
14611             this.el.setXY(this.xy);
14612             this.adjustAssets();
14613         }
14614         return this;
14615     },
14616
14617     /**
14618      * Anchors an element to another element and realigns it when the window is resized.
14619      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14620      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
14621      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14622      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
14623      * is a number, it is used as the buffer delay (defaults to 50ms).
14624      * @return {Roo.BasicDialog} this
14625      */
14626     anchorTo : function(el, alignment, offsets, monitorScroll){
14627         var action = function(){
14628             this.alignTo(el, alignment, offsets);
14629         };
14630         Roo.EventManager.onWindowResize(action, this);
14631         var tm = typeof monitorScroll;
14632         if(tm != 'undefined'){
14633             Roo.EventManager.on(window, 'scroll', action, this,
14634                 {buffer: tm == 'number' ? monitorScroll : 50});
14635         }
14636         action.call(this);
14637         return this;
14638     },
14639
14640     /**
14641      * Returns true if the dialog is visible
14642      * @return {Boolean}
14643      */
14644     isVisible : function(){
14645         return this.el.isVisible();
14646     },
14647
14648     // private
14649     animHide : function(callback){
14650         var b = Roo.get(this.animateTarget).getBox();
14651         this.proxy.show();
14652         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
14653         this.el.hide();
14654         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
14655                     this.hideEl.createDelegate(this, [callback]));
14656     },
14657
14658     /**
14659      * Hides the dialog.
14660      * @param {Function} callback (optional) Function to call when the dialog is hidden
14661      * @return {Roo.BasicDialog} this
14662      */
14663     hide : function(callback){
14664         if (this.fireEvent("beforehide", this) === false){
14665             return;
14666         }
14667         if(this.shadow){
14668             this.shadow.hide();
14669         }
14670         if(this.shim) {
14671           this.shim.hide();
14672         }
14673         // sometimes animateTarget seems to get set.. causing problems...
14674         // this just double checks..
14675         if(this.animateTarget && Roo.get(this.animateTarget)) {
14676            this.animHide(callback);
14677         }else{
14678             this.el.hide();
14679             this.hideEl(callback);
14680         }
14681         return this;
14682     },
14683
14684     // private
14685     hideEl : function(callback){
14686         this.proxy.hide();
14687         if(this.modal){
14688             this.mask.hide();
14689             Roo.get(document.body).removeClass("x-body-masked");
14690         }
14691         this.fireEvent("hide", this);
14692         if(typeof callback == "function"){
14693             callback();
14694         }
14695     },
14696
14697     // private
14698     hideAction : function(){
14699         this.setLeft("-10000px");
14700         this.setTop("-10000px");
14701         this.setStyle("visibility", "hidden");
14702     },
14703
14704     // private
14705     refreshSize : function(){
14706         this.size = this.el.getSize();
14707         this.xy = this.el.getXY();
14708         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
14709     },
14710
14711     // private
14712     // z-index is managed by the DialogManager and may be overwritten at any time
14713     setZIndex : function(index){
14714         if(this.modal){
14715             this.mask.setStyle("z-index", index);
14716         }
14717         if(this.shim){
14718             this.shim.setStyle("z-index", ++index);
14719         }
14720         if(this.shadow){
14721             this.shadow.setZIndex(++index);
14722         }
14723         this.el.setStyle("z-index", ++index);
14724         if(this.proxy){
14725             this.proxy.setStyle("z-index", ++index);
14726         }
14727         if(this.resizer){
14728             this.resizer.proxy.setStyle("z-index", ++index);
14729         }
14730
14731         this.lastZIndex = index;
14732     },
14733
14734     /**
14735      * Returns the element for this dialog
14736      * @return {Roo.Element} The underlying dialog Element
14737      */
14738     getEl : function(){
14739         return this.el;
14740     }
14741 });
14742
14743 /**
14744  * @class Roo.DialogManager
14745  * Provides global access to BasicDialogs that have been created and
14746  * support for z-indexing (layering) multiple open dialogs.
14747  */
14748 Roo.DialogManager = function(){
14749     var list = {};
14750     var accessList = [];
14751     var front = null;
14752
14753     // private
14754     var sortDialogs = function(d1, d2){
14755         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
14756     };
14757
14758     // private
14759     var orderDialogs = function(){
14760         accessList.sort(sortDialogs);
14761         var seed = Roo.DialogManager.zseed;
14762         for(var i = 0, len = accessList.length; i < len; i++){
14763             var dlg = accessList[i];
14764             if(dlg){
14765                 dlg.setZIndex(seed + (i*10));
14766             }
14767         }
14768     };
14769
14770     return {
14771         /**
14772          * The starting z-index for BasicDialogs (defaults to 9000)
14773          * @type Number The z-index value
14774          */
14775         zseed : 9000,
14776
14777         // private
14778         register : function(dlg){
14779             list[dlg.id] = dlg;
14780             accessList.push(dlg);
14781         },
14782
14783         // private
14784         unregister : function(dlg){
14785             delete list[dlg.id];
14786             var i=0;
14787             var len=0;
14788             if(!accessList.indexOf){
14789                 for(  i = 0, len = accessList.length; i < len; i++){
14790                     if(accessList[i] == dlg){
14791                         accessList.splice(i, 1);
14792                         return;
14793                     }
14794                 }
14795             }else{
14796                  i = accessList.indexOf(dlg);
14797                 if(i != -1){
14798                     accessList.splice(i, 1);
14799                 }
14800             }
14801         },
14802
14803         /**
14804          * Gets a registered dialog by id
14805          * @param {String/Object} id The id of the dialog or a dialog
14806          * @return {Roo.BasicDialog} this
14807          */
14808         get : function(id){
14809             return typeof id == "object" ? id : list[id];
14810         },
14811
14812         /**
14813          * Brings the specified dialog to the front
14814          * @param {String/Object} dlg The id of the dialog or a dialog
14815          * @return {Roo.BasicDialog} this
14816          */
14817         bringToFront : function(dlg){
14818             dlg = this.get(dlg);
14819             if(dlg != front){
14820                 front = dlg;
14821                 dlg._lastAccess = new Date().getTime();
14822                 orderDialogs();
14823             }
14824             return dlg;
14825         },
14826
14827         /**
14828          * Sends the specified dialog to the back
14829          * @param {String/Object} dlg The id of the dialog or a dialog
14830          * @return {Roo.BasicDialog} this
14831          */
14832         sendToBack : function(dlg){
14833             dlg = this.get(dlg);
14834             dlg._lastAccess = -(new Date().getTime());
14835             orderDialogs();
14836             return dlg;
14837         },
14838
14839         /**
14840          * Hides all dialogs
14841          */
14842         hideAll : function(){
14843             for(var id in list){
14844                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
14845                     list[id].hide();
14846                 }
14847             }
14848         }
14849     };
14850 }();
14851
14852 /**
14853  * @class Roo.LayoutDialog
14854  * @extends Roo.BasicDialog
14855  * Dialog which provides adjustments for working with a layout in a Dialog.
14856  * Add your necessary layout config options to the dialog's config.<br>
14857  * Example usage (including a nested layout):
14858  * <pre><code>
14859 if(!dialog){
14860     dialog = new Roo.LayoutDialog("download-dlg", {
14861         modal: true,
14862         width:600,
14863         height:450,
14864         shadow:true,
14865         minWidth:500,
14866         minHeight:350,
14867         autoTabs:true,
14868         proxyDrag:true,
14869         // layout config merges with the dialog config
14870         center:{
14871             tabPosition: "top",
14872             alwaysShowTabs: true
14873         }
14874     });
14875     dialog.addKeyListener(27, dialog.hide, dialog);
14876     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
14877     dialog.addButton("Build It!", this.getDownload, this);
14878
14879     // we can even add nested layouts
14880     var innerLayout = new Roo.BorderLayout("dl-inner", {
14881         east: {
14882             initialSize: 200,
14883             autoScroll:true,
14884             split:true
14885         },
14886         center: {
14887             autoScroll:true
14888         }
14889     });
14890     innerLayout.beginUpdate();
14891     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
14892     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
14893     innerLayout.endUpdate(true);
14894
14895     var layout = dialog.getLayout();
14896     layout.beginUpdate();
14897     layout.add("center", new Roo.ContentPanel("standard-panel",
14898                         {title: "Download the Source", fitToFrame:true}));
14899     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
14900                {title: "Build your own roo.js"}));
14901     layout.getRegion("center").showPanel(sp);
14902     layout.endUpdate();
14903 }
14904 </code></pre>
14905     * @constructor
14906     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
14907     * @param {Object} config configuration options
14908   */
14909 Roo.LayoutDialog = function(el, cfg){
14910     
14911     var config=  cfg;
14912     if (typeof(cfg) == 'undefined') {
14913         config = Roo.apply({}, el);
14914         // not sure why we use documentElement here.. - it should always be body.
14915         // IE7 borks horribly if we use documentElement.
14916         // webkit also does not like documentElement - it creates a body element...
14917         el = Roo.get( document.body || document.documentElement ).createChild();
14918         //config.autoCreate = true;
14919     }
14920     
14921     
14922     config.autoTabs = false;
14923     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
14924     this.body.setStyle({overflow:"hidden", position:"relative"});
14925     this.layout = new Roo.BorderLayout(this.body.dom, config);
14926     this.layout.monitorWindowResize = false;
14927     this.el.addClass("x-dlg-auto-layout");
14928     // fix case when center region overwrites center function
14929     this.center = Roo.BasicDialog.prototype.center;
14930     this.on("show", this.layout.layout, this.layout, true);
14931     if (config.items) {
14932         var xitems = config.items;
14933         delete config.items;
14934         Roo.each(xitems, this.addxtype, this);
14935     }
14936     
14937     
14938 };
14939 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
14940     /**
14941      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
14942      * @deprecated
14943      */
14944     endUpdate : function(){
14945         this.layout.endUpdate();
14946     },
14947
14948     /**
14949      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
14950      *  @deprecated
14951      */
14952     beginUpdate : function(){
14953         this.layout.beginUpdate();
14954     },
14955
14956     /**
14957      * Get the BorderLayout for this dialog
14958      * @return {Roo.BorderLayout}
14959      */
14960     getLayout : function(){
14961         return this.layout;
14962     },
14963
14964     showEl : function(){
14965         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
14966         if(Roo.isIE7){
14967             this.layout.layout();
14968         }
14969     },
14970
14971     // private
14972     // Use the syncHeightBeforeShow config option to control this automatically
14973     syncBodyHeight : function(){
14974         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
14975         if(this.layout){this.layout.layout();}
14976     },
14977     
14978       /**
14979      * Add an xtype element (actually adds to the layout.)
14980      * @return {Object} xdata xtype object data.
14981      */
14982     
14983     addxtype : function(c) {
14984         return this.layout.addxtype(c);
14985     }
14986 });/*
14987  * Based on:
14988  * Ext JS Library 1.1.1
14989  * Copyright(c) 2006-2007, Ext JS, LLC.
14990  *
14991  * Originally Released Under LGPL - original licence link has changed is not relivant.
14992  *
14993  * Fork - LGPL
14994  * <script type="text/javascript">
14995  */
14996  
14997 /**
14998  * @class Roo.MessageBox
14999  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15000  * Example usage:
15001  *<pre><code>
15002 // Basic alert:
15003 Roo.Msg.alert('Status', 'Changes saved successfully.');
15004
15005 // Prompt for user data:
15006 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15007     if (btn == 'ok'){
15008         // process text value...
15009     }
15010 });
15011
15012 // Show a dialog using config options:
15013 Roo.Msg.show({
15014    title:'Save Changes?',
15015    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15016    buttons: Roo.Msg.YESNOCANCEL,
15017    fn: processResult,
15018    animEl: 'elId'
15019 });
15020 </code></pre>
15021  * @singleton
15022  */
15023 Roo.MessageBox = function(){
15024     var dlg, opt, mask, waitTimer;
15025     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15026     var buttons, activeTextEl, bwidth;
15027
15028     // private
15029     var handleButton = function(button){
15030         dlg.hide();
15031         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15032     };
15033
15034     // private
15035     var handleHide = function(){
15036         if(opt && opt.cls){
15037             dlg.el.removeClass(opt.cls);
15038         }
15039         if(waitTimer){
15040             Roo.TaskMgr.stop(waitTimer);
15041             waitTimer = null;
15042         }
15043     };
15044
15045     // private
15046     var updateButtons = function(b){
15047         var width = 0;
15048         if(!b){
15049             buttons["ok"].hide();
15050             buttons["cancel"].hide();
15051             buttons["yes"].hide();
15052             buttons["no"].hide();
15053             dlg.footer.dom.style.display = 'none';
15054             return width;
15055         }
15056         dlg.footer.dom.style.display = '';
15057         for(var k in buttons){
15058             if(typeof buttons[k] != "function"){
15059                 if(b[k]){
15060                     buttons[k].show();
15061                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15062                     width += buttons[k].el.getWidth()+15;
15063                 }else{
15064                     buttons[k].hide();
15065                 }
15066             }
15067         }
15068         return width;
15069     };
15070
15071     // private
15072     var handleEsc = function(d, k, e){
15073         if(opt && opt.closable !== false){
15074             dlg.hide();
15075         }
15076         if(e){
15077             e.stopEvent();
15078         }
15079     };
15080
15081     return {
15082         /**
15083          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15084          * @return {Roo.BasicDialog} The BasicDialog element
15085          */
15086         getDialog : function(){
15087            if(!dlg){
15088                 dlg = new Roo.BasicDialog("x-msg-box", {
15089                     autoCreate : true,
15090                     shadow: true,
15091                     draggable: true,
15092                     resizable:false,
15093                     constraintoviewport:false,
15094                     fixedcenter:true,
15095                     collapsible : false,
15096                     shim:true,
15097                     modal: true,
15098                     width:400, height:100,
15099                     buttonAlign:"center",
15100                     closeClick : function(){
15101                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15102                             handleButton("no");
15103                         }else{
15104                             handleButton("cancel");
15105                         }
15106                     }
15107                 });
15108                 dlg.on("hide", handleHide);
15109                 mask = dlg.mask;
15110                 dlg.addKeyListener(27, handleEsc);
15111                 buttons = {};
15112                 var bt = this.buttonText;
15113                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15114                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15115                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15116                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15117                 bodyEl = dlg.body.createChild({
15118
15119                     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>'
15120                 });
15121                 msgEl = bodyEl.dom.firstChild;
15122                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15123                 textboxEl.enableDisplayMode();
15124                 textboxEl.addKeyListener([10,13], function(){
15125                     if(dlg.isVisible() && opt && opt.buttons){
15126                         if(opt.buttons.ok){
15127                             handleButton("ok");
15128                         }else if(opt.buttons.yes){
15129                             handleButton("yes");
15130                         }
15131                     }
15132                 });
15133                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15134                 textareaEl.enableDisplayMode();
15135                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15136                 progressEl.enableDisplayMode();
15137                 var pf = progressEl.dom.firstChild;
15138                 if (pf) {
15139                     pp = Roo.get(pf.firstChild);
15140                     pp.setHeight(pf.offsetHeight);
15141                 }
15142                 
15143             }
15144             return dlg;
15145         },
15146
15147         /**
15148          * Updates the message box body text
15149          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15150          * the XHTML-compliant non-breaking space character '&amp;#160;')
15151          * @return {Roo.MessageBox} This message box
15152          */
15153         updateText : function(text){
15154             if(!dlg.isVisible() && !opt.width){
15155                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15156             }
15157             msgEl.innerHTML = text || '&#160;';
15158       
15159             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15160             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15161             var w = Math.max(
15162                     Math.min(opt.width || cw , this.maxWidth), 
15163                     Math.max(opt.minWidth || this.minWidth, bwidth)
15164             );
15165             if(opt.prompt){
15166                 activeTextEl.setWidth(w);
15167             }
15168             if(dlg.isVisible()){
15169                 dlg.fixedcenter = false;
15170             }
15171             // to big, make it scroll. = But as usual stupid IE does not support
15172             // !important..
15173             
15174             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15175                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15176                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15177             } else {
15178                 bodyEl.dom.style.height = '';
15179                 bodyEl.dom.style.overflowY = '';
15180             }
15181             if (cw > w) {
15182                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15183             } else {
15184                 bodyEl.dom.style.overflowX = '';
15185             }
15186             
15187             dlg.setContentSize(w, bodyEl.getHeight());
15188             if(dlg.isVisible()){
15189                 dlg.fixedcenter = true;
15190             }
15191             return this;
15192         },
15193
15194         /**
15195          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15196          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15197          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15198          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15199          * @return {Roo.MessageBox} This message box
15200          */
15201         updateProgress : function(value, text){
15202             if(text){
15203                 this.updateText(text);
15204             }
15205             if (pp) { // weird bug on my firefox - for some reason this is not defined
15206                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15207             }
15208             return this;
15209         },        
15210
15211         /**
15212          * Returns true if the message box is currently displayed
15213          * @return {Boolean} True if the message box is visible, else false
15214          */
15215         isVisible : function(){
15216             return dlg && dlg.isVisible();  
15217         },
15218
15219         /**
15220          * Hides the message box if it is displayed
15221          */
15222         hide : function(){
15223             if(this.isVisible()){
15224                 dlg.hide();
15225             }  
15226         },
15227
15228         /**
15229          * Displays a new message box, or reinitializes an existing message box, based on the config options
15230          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15231          * The following config object properties are supported:
15232          * <pre>
15233 Property    Type             Description
15234 ----------  ---------------  ------------------------------------------------------------------------------------
15235 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15236                                    closes (defaults to undefined)
15237 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15238                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15239 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15240                                    progress and wait dialogs will ignore this property and always hide the
15241                                    close button as they can only be closed programmatically.
15242 cls               String           A custom CSS class to apply to the message box element
15243 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15244                                    displayed (defaults to 75)
15245 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15246                                    function will be btn (the name of the button that was clicked, if applicable,
15247                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15248                                    Progress and wait dialogs will ignore this option since they do not respond to
15249                                    user actions and can only be closed programmatically, so any required function
15250                                    should be called by the same code after it closes the dialog.
15251 icon              String           A CSS class that provides a background image to be used as an icon for
15252                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15253 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15254 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15255 modal             Boolean          False to allow user interaction with the page while the message box is
15256                                    displayed (defaults to true)
15257 msg               String           A string that will replace the existing message box body text (defaults
15258                                    to the XHTML-compliant non-breaking space character '&#160;')
15259 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15260 progress          Boolean          True to display a progress bar (defaults to false)
15261 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15262 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15263 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15264 title             String           The title text
15265 value             String           The string value to set into the active textbox element if displayed
15266 wait              Boolean          True to display a progress bar (defaults to false)
15267 width             Number           The width of the dialog in pixels
15268 </pre>
15269          *
15270          * Example usage:
15271          * <pre><code>
15272 Roo.Msg.show({
15273    title: 'Address',
15274    msg: 'Please enter your address:',
15275    width: 300,
15276    buttons: Roo.MessageBox.OKCANCEL,
15277    multiline: true,
15278    fn: saveAddress,
15279    animEl: 'addAddressBtn'
15280 });
15281 </code></pre>
15282          * @param {Object} config Configuration options
15283          * @return {Roo.MessageBox} This message box
15284          */
15285         show : function(options)
15286         {
15287             
15288             // this causes nightmares if you show one dialog after another
15289             // especially on callbacks..
15290              
15291             if(this.isVisible()){
15292                 
15293                 this.hide();
15294                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15295                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
15296                 Roo.log("New Dialog Message:" +  options.msg )
15297                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15298                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15299                 
15300             }
15301             var d = this.getDialog();
15302             opt = options;
15303             d.setTitle(opt.title || "&#160;");
15304             d.close.setDisplayed(opt.closable !== false);
15305             activeTextEl = textboxEl;
15306             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15307             if(opt.prompt){
15308                 if(opt.multiline){
15309                     textboxEl.hide();
15310                     textareaEl.show();
15311                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15312                         opt.multiline : this.defaultTextHeight);
15313                     activeTextEl = textareaEl;
15314                 }else{
15315                     textboxEl.show();
15316                     textareaEl.hide();
15317                 }
15318             }else{
15319                 textboxEl.hide();
15320                 textareaEl.hide();
15321             }
15322             progressEl.setDisplayed(opt.progress === true);
15323             this.updateProgress(0);
15324             activeTextEl.dom.value = opt.value || "";
15325             if(opt.prompt){
15326                 dlg.setDefaultButton(activeTextEl);
15327             }else{
15328                 var bs = opt.buttons;
15329                 var db = null;
15330                 if(bs && bs.ok){
15331                     db = buttons["ok"];
15332                 }else if(bs && bs.yes){
15333                     db = buttons["yes"];
15334                 }
15335                 dlg.setDefaultButton(db);
15336             }
15337             bwidth = updateButtons(opt.buttons);
15338             this.updateText(opt.msg);
15339             if(opt.cls){
15340                 d.el.addClass(opt.cls);
15341             }
15342             d.proxyDrag = opt.proxyDrag === true;
15343             d.modal = opt.modal !== false;
15344             d.mask = opt.modal !== false ? mask : false;
15345             if(!d.isVisible()){
15346                 // force it to the end of the z-index stack so it gets a cursor in FF
15347                 document.body.appendChild(dlg.el.dom);
15348                 d.animateTarget = null;
15349                 d.show(options.animEl);
15350             }
15351             return this;
15352         },
15353
15354         /**
15355          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15356          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15357          * and closing the message box when the process is complete.
15358          * @param {String} title The title bar text
15359          * @param {String} msg The message box body text
15360          * @return {Roo.MessageBox} This message box
15361          */
15362         progress : function(title, msg){
15363             this.show({
15364                 title : title,
15365                 msg : msg,
15366                 buttons: false,
15367                 progress:true,
15368                 closable:false,
15369                 minWidth: this.minProgressWidth,
15370                 modal : true
15371             });
15372             return this;
15373         },
15374
15375         /**
15376          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15377          * If a callback function is passed it will be called after the user clicks the button, and the
15378          * id of the button that was clicked will be passed as the only parameter to the callback
15379          * (could also be the top-right close button).
15380          * @param {String} title The title bar text
15381          * @param {String} msg The message box body text
15382          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15383          * @param {Object} scope (optional) The scope of the callback function
15384          * @return {Roo.MessageBox} This message box
15385          */
15386         alert : function(title, msg, fn, scope){
15387             this.show({
15388                 title : title,
15389                 msg : msg,
15390                 buttons: this.OK,
15391                 fn: fn,
15392                 scope : scope,
15393                 modal : true
15394             });
15395             return this;
15396         },
15397
15398         /**
15399          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15400          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15401          * You are responsible for closing the message box when the process is complete.
15402          * @param {String} msg The message box body text
15403          * @param {String} title (optional) The title bar text
15404          * @return {Roo.MessageBox} This message box
15405          */
15406         wait : function(msg, title){
15407             this.show({
15408                 title : title,
15409                 msg : msg,
15410                 buttons: false,
15411                 closable:false,
15412                 progress:true,
15413                 modal:true,
15414                 width:300,
15415                 wait:true
15416             });
15417             waitTimer = Roo.TaskMgr.start({
15418                 run: function(i){
15419                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15420                 },
15421                 interval: 1000
15422             });
15423             return this;
15424         },
15425
15426         /**
15427          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15428          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15429          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15430          * @param {String} title The title bar text
15431          * @param {String} msg The message box body text
15432          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15433          * @param {Object} scope (optional) The scope of the callback function
15434          * @return {Roo.MessageBox} This message box
15435          */
15436         confirm : function(title, msg, fn, scope){
15437             this.show({
15438                 title : title,
15439                 msg : msg,
15440                 buttons: this.YESNO,
15441                 fn: fn,
15442                 scope : scope,
15443                 modal : true
15444             });
15445             return this;
15446         },
15447
15448         /**
15449          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15450          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15451          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15452          * (could also be the top-right close button) and the text that was entered will be passed as the two
15453          * parameters to the callback.
15454          * @param {String} title The title bar text
15455          * @param {String} msg The message box body text
15456          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15457          * @param {Object} scope (optional) The scope of the callback function
15458          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15459          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15460          * @return {Roo.MessageBox} This message box
15461          */
15462         prompt : function(title, msg, fn, scope, multiline){
15463             this.show({
15464                 title : title,
15465                 msg : msg,
15466                 buttons: this.OKCANCEL,
15467                 fn: fn,
15468                 minWidth:250,
15469                 scope : scope,
15470                 prompt:true,
15471                 multiline: multiline,
15472                 modal : true
15473             });
15474             return this;
15475         },
15476
15477         /**
15478          * Button config that displays a single OK button
15479          * @type Object
15480          */
15481         OK : {ok:true},
15482         /**
15483          * Button config that displays Yes and No buttons
15484          * @type Object
15485          */
15486         YESNO : {yes:true, no:true},
15487         /**
15488          * Button config that displays OK and Cancel buttons
15489          * @type Object
15490          */
15491         OKCANCEL : {ok:true, cancel:true},
15492         /**
15493          * Button config that displays Yes, No and Cancel buttons
15494          * @type Object
15495          */
15496         YESNOCANCEL : {yes:true, no:true, cancel:true},
15497
15498         /**
15499          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15500          * @type Number
15501          */
15502         defaultTextHeight : 75,
15503         /**
15504          * The maximum width in pixels of the message box (defaults to 600)
15505          * @type Number
15506          */
15507         maxWidth : 600,
15508         /**
15509          * The minimum width in pixels of the message box (defaults to 100)
15510          * @type Number
15511          */
15512         minWidth : 100,
15513         /**
15514          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15515          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15516          * @type Number
15517          */
15518         minProgressWidth : 250,
15519         /**
15520          * An object containing the default button text strings that can be overriden for localized language support.
15521          * Supported properties are: ok, cancel, yes and no.
15522          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15523          * @type Object
15524          */
15525         buttonText : {
15526             ok : "OK",
15527             cancel : "Cancel",
15528             yes : "Yes",
15529             no : "No"
15530         }
15531     };
15532 }();
15533
15534 /**
15535  * Shorthand for {@link Roo.MessageBox}
15536  */
15537 Roo.Msg = Roo.MessageBox;/*
15538  * Based on:
15539  * Ext JS Library 1.1.1
15540  * Copyright(c) 2006-2007, Ext JS, LLC.
15541  *
15542  * Originally Released Under LGPL - original licence link has changed is not relivant.
15543  *
15544  * Fork - LGPL
15545  * <script type="text/javascript">
15546  */
15547 /**
15548  * @class Roo.QuickTips
15549  * Provides attractive and customizable tooltips for any element.
15550  * @singleton
15551  */
15552 Roo.QuickTips = function(){
15553     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15554     var ce, bd, xy, dd;
15555     var visible = false, disabled = true, inited = false;
15556     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15557     
15558     var onOver = function(e){
15559         if(disabled){
15560             return;
15561         }
15562         var t = e.getTarget();
15563         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15564             return;
15565         }
15566         if(ce && t == ce.el){
15567             clearTimeout(hideProc);
15568             return;
15569         }
15570         if(t && tagEls[t.id]){
15571             tagEls[t.id].el = t;
15572             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15573             return;
15574         }
15575         var ttp, et = Roo.fly(t);
15576         var ns = cfg.namespace;
15577         if(tm.interceptTitles && t.title){
15578             ttp = t.title;
15579             t.qtip = ttp;
15580             t.removeAttribute("title");
15581             e.preventDefault();
15582         }else{
15583             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
15584         }
15585         if(ttp){
15586             showProc = show.defer(tm.showDelay, tm, [{
15587                 el: t, 
15588                 text: ttp, 
15589                 width: et.getAttributeNS(ns, cfg.width),
15590                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15591                 title: et.getAttributeNS(ns, cfg.title),
15592                     cls: et.getAttributeNS(ns, cfg.cls)
15593             }]);
15594         }
15595     };
15596     
15597     var onOut = function(e){
15598         clearTimeout(showProc);
15599         var t = e.getTarget();
15600         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15601             hideProc = setTimeout(hide, tm.hideDelay);
15602         }
15603     };
15604     
15605     var onMove = function(e){
15606         if(disabled){
15607             return;
15608         }
15609         xy = e.getXY();
15610         xy[1] += 18;
15611         if(tm.trackMouse && ce){
15612             el.setXY(xy);
15613         }
15614     };
15615     
15616     var onDown = function(e){
15617         clearTimeout(showProc);
15618         clearTimeout(hideProc);
15619         if(!e.within(el)){
15620             if(tm.hideOnClick){
15621                 hide();
15622                 tm.disable();
15623                 tm.enable.defer(100, tm);
15624             }
15625         }
15626     };
15627     
15628     var getPad = function(){
15629         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
15630     };
15631
15632     var show = function(o){
15633         if(disabled){
15634             return;
15635         }
15636         clearTimeout(dismissProc);
15637         ce = o;
15638         if(removeCls){ // in case manually hidden
15639             el.removeClass(removeCls);
15640             removeCls = null;
15641         }
15642         if(ce.cls){
15643             el.addClass(ce.cls);
15644             removeCls = ce.cls;
15645         }
15646         if(ce.title){
15647             tipTitle.update(ce.title);
15648             tipTitle.show();
15649         }else{
15650             tipTitle.update('');
15651             tipTitle.hide();
15652         }
15653         el.dom.style.width  = tm.maxWidth+'px';
15654         //tipBody.dom.style.width = '';
15655         tipBodyText.update(o.text);
15656         var p = getPad(), w = ce.width;
15657         if(!w){
15658             var td = tipBodyText.dom;
15659             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
15660             if(aw > tm.maxWidth){
15661                 w = tm.maxWidth;
15662             }else if(aw < tm.minWidth){
15663                 w = tm.minWidth;
15664             }else{
15665                 w = aw;
15666             }
15667         }
15668         //tipBody.setWidth(w);
15669         el.setWidth(parseInt(w, 10) + p);
15670         if(ce.autoHide === false){
15671             close.setDisplayed(true);
15672             if(dd){
15673                 dd.unlock();
15674             }
15675         }else{
15676             close.setDisplayed(false);
15677             if(dd){
15678                 dd.lock();
15679             }
15680         }
15681         if(xy){
15682             el.avoidY = xy[1]-18;
15683             el.setXY(xy);
15684         }
15685         if(tm.animate){
15686             el.setOpacity(.1);
15687             el.setStyle("visibility", "visible");
15688             el.fadeIn({callback: afterShow});
15689         }else{
15690             afterShow();
15691         }
15692     };
15693     
15694     var afterShow = function(){
15695         if(ce){
15696             el.show();
15697             esc.enable();
15698             if(tm.autoDismiss && ce.autoHide !== false){
15699                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
15700             }
15701         }
15702     };
15703     
15704     var hide = function(noanim){
15705         clearTimeout(dismissProc);
15706         clearTimeout(hideProc);
15707         ce = null;
15708         if(el.isVisible()){
15709             esc.disable();
15710             if(noanim !== true && tm.animate){
15711                 el.fadeOut({callback: afterHide});
15712             }else{
15713                 afterHide();
15714             } 
15715         }
15716     };
15717     
15718     var afterHide = function(){
15719         el.hide();
15720         if(removeCls){
15721             el.removeClass(removeCls);
15722             removeCls = null;
15723         }
15724     };
15725     
15726     return {
15727         /**
15728         * @cfg {Number} minWidth
15729         * The minimum width of the quick tip (defaults to 40)
15730         */
15731        minWidth : 40,
15732         /**
15733         * @cfg {Number} maxWidth
15734         * The maximum width of the quick tip (defaults to 300)
15735         */
15736        maxWidth : 300,
15737         /**
15738         * @cfg {Boolean} interceptTitles
15739         * True to automatically use the element's DOM title value if available (defaults to false)
15740         */
15741        interceptTitles : false,
15742         /**
15743         * @cfg {Boolean} trackMouse
15744         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
15745         */
15746        trackMouse : false,
15747         /**
15748         * @cfg {Boolean} hideOnClick
15749         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
15750         */
15751        hideOnClick : true,
15752         /**
15753         * @cfg {Number} showDelay
15754         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
15755         */
15756        showDelay : 500,
15757         /**
15758         * @cfg {Number} hideDelay
15759         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
15760         */
15761        hideDelay : 200,
15762         /**
15763         * @cfg {Boolean} autoHide
15764         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
15765         * Used in conjunction with hideDelay.
15766         */
15767        autoHide : true,
15768         /**
15769         * @cfg {Boolean}
15770         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
15771         * (defaults to true).  Used in conjunction with autoDismissDelay.
15772         */
15773        autoDismiss : true,
15774         /**
15775         * @cfg {Number}
15776         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
15777         */
15778        autoDismissDelay : 5000,
15779        /**
15780         * @cfg {Boolean} animate
15781         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
15782         */
15783        animate : false,
15784
15785        /**
15786         * @cfg {String} title
15787         * Title text to display (defaults to '').  This can be any valid HTML markup.
15788         */
15789         title: '',
15790        /**
15791         * @cfg {String} text
15792         * Body text to display (defaults to '').  This can be any valid HTML markup.
15793         */
15794         text : '',
15795        /**
15796         * @cfg {String} cls
15797         * A CSS class to apply to the base quick tip element (defaults to '').
15798         */
15799         cls : '',
15800        /**
15801         * @cfg {Number} width
15802         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
15803         * minWidth or maxWidth.
15804         */
15805         width : null,
15806
15807     /**
15808      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
15809      * or display QuickTips in a page.
15810      */
15811        init : function(){
15812           tm = Roo.QuickTips;
15813           cfg = tm.tagConfig;
15814           if(!inited){
15815               if(!Roo.isReady){ // allow calling of init() before onReady
15816                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
15817                   return;
15818               }
15819               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
15820               el.fxDefaults = {stopFx: true};
15821               // maximum custom styling
15822               //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>');
15823               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>');              
15824               tipTitle = el.child('h3');
15825               tipTitle.enableDisplayMode("block");
15826               tipBody = el.child('div.x-tip-bd');
15827               tipBodyText = el.child('div.x-tip-bd-inner');
15828               //bdLeft = el.child('div.x-tip-bd-left');
15829               //bdRight = el.child('div.x-tip-bd-right');
15830               close = el.child('div.x-tip-close');
15831               close.enableDisplayMode("block");
15832               close.on("click", hide);
15833               var d = Roo.get(document);
15834               d.on("mousedown", onDown);
15835               d.on("mouseover", onOver);
15836               d.on("mouseout", onOut);
15837               d.on("mousemove", onMove);
15838               esc = d.addKeyListener(27, hide);
15839               esc.disable();
15840               if(Roo.dd.DD){
15841                   dd = el.initDD("default", null, {
15842                       onDrag : function(){
15843                           el.sync();  
15844                       }
15845                   });
15846                   dd.setHandleElId(tipTitle.id);
15847                   dd.lock();
15848               }
15849               inited = true;
15850           }
15851           this.enable(); 
15852        },
15853
15854     /**
15855      * Configures a new quick tip instance and assigns it to a target element.  The following config options
15856      * are supported:
15857      * <pre>
15858 Property    Type                   Description
15859 ----------  ---------------------  ------------------------------------------------------------------------
15860 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
15861      * </ul>
15862      * @param {Object} config The config object
15863      */
15864        register : function(config){
15865            var cs = config instanceof Array ? config : arguments;
15866            for(var i = 0, len = cs.length; i < len; i++) {
15867                var c = cs[i];
15868                var target = c.target;
15869                if(target){
15870                    if(target instanceof Array){
15871                        for(var j = 0, jlen = target.length; j < jlen; j++){
15872                            tagEls[target[j]] = c;
15873                        }
15874                    }else{
15875                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
15876                    }
15877                }
15878            }
15879        },
15880
15881     /**
15882      * Removes this quick tip from its element and destroys it.
15883      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
15884      */
15885        unregister : function(el){
15886            delete tagEls[Roo.id(el)];
15887        },
15888
15889     /**
15890      * Enable this quick tip.
15891      */
15892        enable : function(){
15893            if(inited && disabled){
15894                locks.pop();
15895                if(locks.length < 1){
15896                    disabled = false;
15897                }
15898            }
15899        },
15900
15901     /**
15902      * Disable this quick tip.
15903      */
15904        disable : function(){
15905           disabled = true;
15906           clearTimeout(showProc);
15907           clearTimeout(hideProc);
15908           clearTimeout(dismissProc);
15909           if(ce){
15910               hide(true);
15911           }
15912           locks.push(1);
15913        },
15914
15915     /**
15916      * Returns true if the quick tip is enabled, else false.
15917      */
15918        isEnabled : function(){
15919             return !disabled;
15920        },
15921
15922         // private
15923        tagConfig : {
15924            namespace : "roo", // was ext?? this may break..
15925            alt_namespace : "ext",
15926            attribute : "qtip",
15927            width : "width",
15928            target : "target",
15929            title : "qtitle",
15930            hide : "hide",
15931            cls : "qclass"
15932        }
15933    };
15934 }();
15935
15936 // backwards compat
15937 Roo.QuickTips.tips = Roo.QuickTips.register;/*
15938  * Based on:
15939  * Ext JS Library 1.1.1
15940  * Copyright(c) 2006-2007, Ext JS, LLC.
15941  *
15942  * Originally Released Under LGPL - original licence link has changed is not relivant.
15943  *
15944  * Fork - LGPL
15945  * <script type="text/javascript">
15946  */
15947  
15948
15949 /**
15950  * @class Roo.tree.TreePanel
15951  * @extends Roo.data.Tree
15952
15953  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
15954  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
15955  * @cfg {Boolean} enableDD true to enable drag and drop
15956  * @cfg {Boolean} enableDrag true to enable just drag
15957  * @cfg {Boolean} enableDrop true to enable just drop
15958  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
15959  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
15960  * @cfg {String} ddGroup The DD group this TreePanel belongs to
15961  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
15962  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
15963  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
15964  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
15965  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
15966  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
15967  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
15968  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
15969  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
15970  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
15971  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
15972  * @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>
15973  * @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>
15974  * 
15975  * @constructor
15976  * @param {String/HTMLElement/Element} el The container element
15977  * @param {Object} config
15978  */
15979 Roo.tree.TreePanel = function(el, config){
15980     var root = false;
15981     var loader = false;
15982     if (config.root) {
15983         root = config.root;
15984         delete config.root;
15985     }
15986     if (config.loader) {
15987         loader = config.loader;
15988         delete config.loader;
15989     }
15990     
15991     Roo.apply(this, config);
15992     Roo.tree.TreePanel.superclass.constructor.call(this);
15993     this.el = Roo.get(el);
15994     this.el.addClass('x-tree');
15995     //console.log(root);
15996     if (root) {
15997         this.setRootNode( Roo.factory(root, Roo.tree));
15998     }
15999     if (loader) {
16000         this.loader = Roo.factory(loader, Roo.tree);
16001     }
16002    /**
16003     * Read-only. The id of the container element becomes this TreePanel's id.
16004     */
16005     this.id = this.el.id;
16006     this.addEvents({
16007         /**
16008         * @event beforeload
16009         * Fires before a node is loaded, return false to cancel
16010         * @param {Node} node The node being loaded
16011         */
16012         "beforeload" : true,
16013         /**
16014         * @event load
16015         * Fires when a node is loaded
16016         * @param {Node} node The node that was loaded
16017         */
16018         "load" : true,
16019         /**
16020         * @event textchange
16021         * Fires when the text for a node is changed
16022         * @param {Node} node The node
16023         * @param {String} text The new text
16024         * @param {String} oldText The old text
16025         */
16026         "textchange" : true,
16027         /**
16028         * @event beforeexpand
16029         * Fires before a node is expanded, return false to cancel.
16030         * @param {Node} node The node
16031         * @param {Boolean} deep
16032         * @param {Boolean} anim
16033         */
16034         "beforeexpand" : true,
16035         /**
16036         * @event beforecollapse
16037         * Fires before a node is collapsed, return false to cancel.
16038         * @param {Node} node The node
16039         * @param {Boolean} deep
16040         * @param {Boolean} anim
16041         */
16042         "beforecollapse" : true,
16043         /**
16044         * @event expand
16045         * Fires when a node is expanded
16046         * @param {Node} node The node
16047         */
16048         "expand" : true,
16049         /**
16050         * @event disabledchange
16051         * Fires when the disabled status of a node changes
16052         * @param {Node} node The node
16053         * @param {Boolean} disabled
16054         */
16055         "disabledchange" : true,
16056         /**
16057         * @event collapse
16058         * Fires when a node is collapsed
16059         * @param {Node} node The node
16060         */
16061         "collapse" : true,
16062         /**
16063         * @event beforeclick
16064         * Fires before click processing on a node. Return false to cancel the default action.
16065         * @param {Node} node The node
16066         * @param {Roo.EventObject} e The event object
16067         */
16068         "beforeclick":true,
16069         /**
16070         * @event checkchange
16071         * Fires when a node with a checkbox's checked property changes
16072         * @param {Node} this This node
16073         * @param {Boolean} checked
16074         */
16075         "checkchange":true,
16076         /**
16077         * @event click
16078         * Fires when a node is clicked
16079         * @param {Node} node The node
16080         * @param {Roo.EventObject} e The event object
16081         */
16082         "click":true,
16083         /**
16084         * @event dblclick
16085         * Fires when a node is double clicked
16086         * @param {Node} node The node
16087         * @param {Roo.EventObject} e The event object
16088         */
16089         "dblclick":true,
16090         /**
16091         * @event contextmenu
16092         * Fires when a node is right clicked
16093         * @param {Node} node The node
16094         * @param {Roo.EventObject} e The event object
16095         */
16096         "contextmenu":true,
16097         /**
16098         * @event beforechildrenrendered
16099         * Fires right before the child nodes for a node are rendered
16100         * @param {Node} node The node
16101         */
16102         "beforechildrenrendered":true,
16103         /**
16104         * @event startdrag
16105         * Fires when a node starts being dragged
16106         * @param {Roo.tree.TreePanel} this
16107         * @param {Roo.tree.TreeNode} node
16108         * @param {event} e The raw browser event
16109         */ 
16110        "startdrag" : true,
16111        /**
16112         * @event enddrag
16113         * Fires when a drag operation is complete
16114         * @param {Roo.tree.TreePanel} this
16115         * @param {Roo.tree.TreeNode} node
16116         * @param {event} e The raw browser event
16117         */
16118        "enddrag" : true,
16119        /**
16120         * @event dragdrop
16121         * Fires when a dragged node is dropped on a valid DD target
16122         * @param {Roo.tree.TreePanel} this
16123         * @param {Roo.tree.TreeNode} node
16124         * @param {DD} dd The dd it was dropped on
16125         * @param {event} e The raw browser event
16126         */
16127        "dragdrop" : true,
16128        /**
16129         * @event beforenodedrop
16130         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16131         * passed to handlers has the following properties:<br />
16132         * <ul style="padding:5px;padding-left:16px;">
16133         * <li>tree - The TreePanel</li>
16134         * <li>target - The node being targeted for the drop</li>
16135         * <li>data - The drag data from the drag source</li>
16136         * <li>point - The point of the drop - append, above or below</li>
16137         * <li>source - The drag source</li>
16138         * <li>rawEvent - Raw mouse event</li>
16139         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16140         * to be inserted by setting them on this object.</li>
16141         * <li>cancel - Set this to true to cancel the drop.</li>
16142         * </ul>
16143         * @param {Object} dropEvent
16144         */
16145        "beforenodedrop" : true,
16146        /**
16147         * @event nodedrop
16148         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16149         * passed to handlers has the following properties:<br />
16150         * <ul style="padding:5px;padding-left:16px;">
16151         * <li>tree - The TreePanel</li>
16152         * <li>target - The node being targeted for the drop</li>
16153         * <li>data - The drag data from the drag source</li>
16154         * <li>point - The point of the drop - append, above or below</li>
16155         * <li>source - The drag source</li>
16156         * <li>rawEvent - Raw mouse event</li>
16157         * <li>dropNode - Dropped node(s).</li>
16158         * </ul>
16159         * @param {Object} dropEvent
16160         */
16161        "nodedrop" : true,
16162         /**
16163         * @event nodedragover
16164         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16165         * passed to handlers has the following properties:<br />
16166         * <ul style="padding:5px;padding-left:16px;">
16167         * <li>tree - The TreePanel</li>
16168         * <li>target - The node being targeted for the drop</li>
16169         * <li>data - The drag data from the drag source</li>
16170         * <li>point - The point of the drop - append, above or below</li>
16171         * <li>source - The drag source</li>
16172         * <li>rawEvent - Raw mouse event</li>
16173         * <li>dropNode - Drop node(s) provided by the source.</li>
16174         * <li>cancel - Set this to true to signal drop not allowed.</li>
16175         * </ul>
16176         * @param {Object} dragOverEvent
16177         */
16178        "nodedragover" : true
16179         
16180     });
16181     if(this.singleExpand){
16182        this.on("beforeexpand", this.restrictExpand, this);
16183     }
16184     if (this.editor) {
16185         this.editor.tree = this;
16186         this.editor = Roo.factory(this.editor, Roo.tree);
16187     }
16188     
16189     if (this.selModel) {
16190         this.selModel = Roo.factory(this.selModel, Roo.tree);
16191     }
16192    
16193 };
16194 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16195     rootVisible : true,
16196     animate: Roo.enableFx,
16197     lines : true,
16198     enableDD : false,
16199     hlDrop : Roo.enableFx,
16200   
16201     renderer: false,
16202     
16203     rendererTip: false,
16204     // private
16205     restrictExpand : function(node){
16206         var p = node.parentNode;
16207         if(p){
16208             if(p.expandedChild && p.expandedChild.parentNode == p){
16209                 p.expandedChild.collapse();
16210             }
16211             p.expandedChild = node;
16212         }
16213     },
16214
16215     // private override
16216     setRootNode : function(node){
16217         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16218         if(!this.rootVisible){
16219             node.ui = new Roo.tree.RootTreeNodeUI(node);
16220         }
16221         return node;
16222     },
16223
16224     /**
16225      * Returns the container element for this TreePanel
16226      */
16227     getEl : function(){
16228         return this.el;
16229     },
16230
16231     /**
16232      * Returns the default TreeLoader for this TreePanel
16233      */
16234     getLoader : function(){
16235         return this.loader;
16236     },
16237
16238     /**
16239      * Expand all nodes
16240      */
16241     expandAll : function(){
16242         this.root.expand(true);
16243     },
16244
16245     /**
16246      * Collapse all nodes
16247      */
16248     collapseAll : function(){
16249         this.root.collapse(true);
16250     },
16251
16252     /**
16253      * Returns the selection model used by this TreePanel
16254      */
16255     getSelectionModel : function(){
16256         if(!this.selModel){
16257             this.selModel = new Roo.tree.DefaultSelectionModel();
16258         }
16259         return this.selModel;
16260     },
16261
16262     /**
16263      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16264      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16265      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16266      * @return {Array}
16267      */
16268     getChecked : function(a, startNode){
16269         startNode = startNode || this.root;
16270         var r = [];
16271         var f = function(){
16272             if(this.attributes.checked){
16273                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16274             }
16275         }
16276         startNode.cascade(f);
16277         return r;
16278     },
16279
16280     /**
16281      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16282      * @param {String} path
16283      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16284      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16285      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16286      */
16287     expandPath : function(path, attr, callback){
16288         attr = attr || "id";
16289         var keys = path.split(this.pathSeparator);
16290         var curNode = this.root;
16291         if(curNode.attributes[attr] != keys[1]){ // invalid root
16292             if(callback){
16293                 callback(false, null);
16294             }
16295             return;
16296         }
16297         var index = 1;
16298         var f = function(){
16299             if(++index == keys.length){
16300                 if(callback){
16301                     callback(true, curNode);
16302                 }
16303                 return;
16304             }
16305             var c = curNode.findChild(attr, keys[index]);
16306             if(!c){
16307                 if(callback){
16308                     callback(false, curNode);
16309                 }
16310                 return;
16311             }
16312             curNode = c;
16313             c.expand(false, false, f);
16314         };
16315         curNode.expand(false, false, f);
16316     },
16317
16318     /**
16319      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16320      * @param {String} path
16321      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16322      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16323      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16324      */
16325     selectPath : function(path, attr, callback){
16326         attr = attr || "id";
16327         var keys = path.split(this.pathSeparator);
16328         var v = keys.pop();
16329         if(keys.length > 0){
16330             var f = function(success, node){
16331                 if(success && node){
16332                     var n = node.findChild(attr, v);
16333                     if(n){
16334                         n.select();
16335                         if(callback){
16336                             callback(true, n);
16337                         }
16338                     }else if(callback){
16339                         callback(false, n);
16340                     }
16341                 }else{
16342                     if(callback){
16343                         callback(false, n);
16344                     }
16345                 }
16346             };
16347             this.expandPath(keys.join(this.pathSeparator), attr, f);
16348         }else{
16349             this.root.select();
16350             if(callback){
16351                 callback(true, this.root);
16352             }
16353         }
16354     },
16355
16356     getTreeEl : function(){
16357         return this.el;
16358     },
16359
16360     /**
16361      * Trigger rendering of this TreePanel
16362      */
16363     render : function(){
16364         if (this.innerCt) {
16365             return this; // stop it rendering more than once!!
16366         }
16367         
16368         this.innerCt = this.el.createChild({tag:"ul",
16369                cls:"x-tree-root-ct " +
16370                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16371
16372         if(this.containerScroll){
16373             Roo.dd.ScrollManager.register(this.el);
16374         }
16375         if((this.enableDD || this.enableDrop) && !this.dropZone){
16376            /**
16377             * The dropZone used by this tree if drop is enabled
16378             * @type Roo.tree.TreeDropZone
16379             */
16380              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16381                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16382            });
16383         }
16384         if((this.enableDD || this.enableDrag) && !this.dragZone){
16385            /**
16386             * The dragZone used by this tree if drag is enabled
16387             * @type Roo.tree.TreeDragZone
16388             */
16389             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16390                ddGroup: this.ddGroup || "TreeDD",
16391                scroll: this.ddScroll
16392            });
16393         }
16394         this.getSelectionModel().init(this);
16395         if (!this.root) {
16396             Roo.log("ROOT not set in tree");
16397             return this;
16398         }
16399         this.root.render();
16400         if(!this.rootVisible){
16401             this.root.renderChildren();
16402         }
16403         return this;
16404     }
16405 });/*
16406  * Based on:
16407  * Ext JS Library 1.1.1
16408  * Copyright(c) 2006-2007, Ext JS, LLC.
16409  *
16410  * Originally Released Under LGPL - original licence link has changed is not relivant.
16411  *
16412  * Fork - LGPL
16413  * <script type="text/javascript">
16414  */
16415  
16416
16417 /**
16418  * @class Roo.tree.DefaultSelectionModel
16419  * @extends Roo.util.Observable
16420  * The default single selection for a TreePanel.
16421  * @param {Object} cfg Configuration
16422  */
16423 Roo.tree.DefaultSelectionModel = function(cfg){
16424    this.selNode = null;
16425    
16426    
16427    
16428    this.addEvents({
16429        /**
16430         * @event selectionchange
16431         * Fires when the selected node changes
16432         * @param {DefaultSelectionModel} this
16433         * @param {TreeNode} node the new selection
16434         */
16435        "selectionchange" : true,
16436
16437        /**
16438         * @event beforeselect
16439         * Fires before the selected node changes, return false to cancel the change
16440         * @param {DefaultSelectionModel} this
16441         * @param {TreeNode} node the new selection
16442         * @param {TreeNode} node the old selection
16443         */
16444        "beforeselect" : true
16445    });
16446    
16447     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
16448 };
16449
16450 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16451     init : function(tree){
16452         this.tree = tree;
16453         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16454         tree.on("click", this.onNodeClick, this);
16455     },
16456     
16457     onNodeClick : function(node, e){
16458         if (e.ctrlKey && this.selNode == node)  {
16459             this.unselect(node);
16460             return;
16461         }
16462         this.select(node);
16463     },
16464     
16465     /**
16466      * Select a node.
16467      * @param {TreeNode} node The node to select
16468      * @return {TreeNode} The selected node
16469      */
16470     select : function(node){
16471         var last = this.selNode;
16472         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16473             if(last){
16474                 last.ui.onSelectedChange(false);
16475             }
16476             this.selNode = node;
16477             node.ui.onSelectedChange(true);
16478             this.fireEvent("selectionchange", this, node, last);
16479         }
16480         return node;
16481     },
16482     
16483     /**
16484      * Deselect a node.
16485      * @param {TreeNode} node The node to unselect
16486      */
16487     unselect : function(node){
16488         if(this.selNode == node){
16489             this.clearSelections();
16490         }    
16491     },
16492     
16493     /**
16494      * Clear all selections
16495      */
16496     clearSelections : function(){
16497         var n = this.selNode;
16498         if(n){
16499             n.ui.onSelectedChange(false);
16500             this.selNode = null;
16501             this.fireEvent("selectionchange", this, null);
16502         }
16503         return n;
16504     },
16505     
16506     /**
16507      * Get the selected node
16508      * @return {TreeNode} The selected node
16509      */
16510     getSelectedNode : function(){
16511         return this.selNode;    
16512     },
16513     
16514     /**
16515      * Returns true if the node is selected
16516      * @param {TreeNode} node The node to check
16517      * @return {Boolean}
16518      */
16519     isSelected : function(node){
16520         return this.selNode == node;  
16521     },
16522
16523     /**
16524      * Selects the node above the selected node in the tree, intelligently walking the nodes
16525      * @return TreeNode The new selection
16526      */
16527     selectPrevious : function(){
16528         var s = this.selNode || this.lastSelNode;
16529         if(!s){
16530             return null;
16531         }
16532         var ps = s.previousSibling;
16533         if(ps){
16534             if(!ps.isExpanded() || ps.childNodes.length < 1){
16535                 return this.select(ps);
16536             } else{
16537                 var lc = ps.lastChild;
16538                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16539                     lc = lc.lastChild;
16540                 }
16541                 return this.select(lc);
16542             }
16543         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16544             return this.select(s.parentNode);
16545         }
16546         return null;
16547     },
16548
16549     /**
16550      * Selects the node above the selected node in the tree, intelligently walking the nodes
16551      * @return TreeNode The new selection
16552      */
16553     selectNext : function(){
16554         var s = this.selNode || this.lastSelNode;
16555         if(!s){
16556             return null;
16557         }
16558         if(s.firstChild && s.isExpanded()){
16559              return this.select(s.firstChild);
16560          }else if(s.nextSibling){
16561              return this.select(s.nextSibling);
16562          }else if(s.parentNode){
16563             var newS = null;
16564             s.parentNode.bubble(function(){
16565                 if(this.nextSibling){
16566                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16567                     return false;
16568                 }
16569             });
16570             return newS;
16571          }
16572         return null;
16573     },
16574
16575     onKeyDown : function(e){
16576         var s = this.selNode || this.lastSelNode;
16577         // undesirable, but required
16578         var sm = this;
16579         if(!s){
16580             return;
16581         }
16582         var k = e.getKey();
16583         switch(k){
16584              case e.DOWN:
16585                  e.stopEvent();
16586                  this.selectNext();
16587              break;
16588              case e.UP:
16589                  e.stopEvent();
16590                  this.selectPrevious();
16591              break;
16592              case e.RIGHT:
16593                  e.preventDefault();
16594                  if(s.hasChildNodes()){
16595                      if(!s.isExpanded()){
16596                          s.expand();
16597                      }else if(s.firstChild){
16598                          this.select(s.firstChild, e);
16599                      }
16600                  }
16601              break;
16602              case e.LEFT:
16603                  e.preventDefault();
16604                  if(s.hasChildNodes() && s.isExpanded()){
16605                      s.collapse();
16606                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16607                      this.select(s.parentNode, e);
16608                  }
16609              break;
16610         };
16611     }
16612 });
16613
16614 /**
16615  * @class Roo.tree.MultiSelectionModel
16616  * @extends Roo.util.Observable
16617  * Multi selection for a TreePanel.
16618  * @param {Object} cfg Configuration
16619  */
16620 Roo.tree.MultiSelectionModel = function(){
16621    this.selNodes = [];
16622    this.selMap = {};
16623    this.addEvents({
16624        /**
16625         * @event selectionchange
16626         * Fires when the selected nodes change
16627         * @param {MultiSelectionModel} this
16628         * @param {Array} nodes Array of the selected nodes
16629         */
16630        "selectionchange" : true
16631    });
16632    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
16633    
16634 };
16635
16636 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16637     init : function(tree){
16638         this.tree = tree;
16639         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16640         tree.on("click", this.onNodeClick, this);
16641     },
16642     
16643     onNodeClick : function(node, e){
16644         this.select(node, e, e.ctrlKey);
16645     },
16646     
16647     /**
16648      * Select a node.
16649      * @param {TreeNode} node The node to select
16650      * @param {EventObject} e (optional) An event associated with the selection
16651      * @param {Boolean} keepExisting True to retain existing selections
16652      * @return {TreeNode} The selected node
16653      */
16654     select : function(node, e, keepExisting){
16655         if(keepExisting !== true){
16656             this.clearSelections(true);
16657         }
16658         if(this.isSelected(node)){
16659             this.lastSelNode = node;
16660             return node;
16661         }
16662         this.selNodes.push(node);
16663         this.selMap[node.id] = node;
16664         this.lastSelNode = node;
16665         node.ui.onSelectedChange(true);
16666         this.fireEvent("selectionchange", this, this.selNodes);
16667         return node;
16668     },
16669     
16670     /**
16671      * Deselect a node.
16672      * @param {TreeNode} node The node to unselect
16673      */
16674     unselect : function(node){
16675         if(this.selMap[node.id]){
16676             node.ui.onSelectedChange(false);
16677             var sn = this.selNodes;
16678             var index = -1;
16679             if(sn.indexOf){
16680                 index = sn.indexOf(node);
16681             }else{
16682                 for(var i = 0, len = sn.length; i < len; i++){
16683                     if(sn[i] == node){
16684                         index = i;
16685                         break;
16686                     }
16687                 }
16688             }
16689             if(index != -1){
16690                 this.selNodes.splice(index, 1);
16691             }
16692             delete this.selMap[node.id];
16693             this.fireEvent("selectionchange", this, this.selNodes);
16694         }
16695     },
16696     
16697     /**
16698      * Clear all selections
16699      */
16700     clearSelections : function(suppressEvent){
16701         var sn = this.selNodes;
16702         if(sn.length > 0){
16703             for(var i = 0, len = sn.length; i < len; i++){
16704                 sn[i].ui.onSelectedChange(false);
16705             }
16706             this.selNodes = [];
16707             this.selMap = {};
16708             if(suppressEvent !== true){
16709                 this.fireEvent("selectionchange", this, this.selNodes);
16710             }
16711         }
16712     },
16713     
16714     /**
16715      * Returns true if the node is selected
16716      * @param {TreeNode} node The node to check
16717      * @return {Boolean}
16718      */
16719     isSelected : function(node){
16720         return this.selMap[node.id] ? true : false;  
16721     },
16722     
16723     /**
16724      * Returns an array of the selected nodes
16725      * @return {Array}
16726      */
16727     getSelectedNodes : function(){
16728         return this.selNodes;    
16729     },
16730
16731     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
16732
16733     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
16734
16735     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
16736 });/*
16737  * Based on:
16738  * Ext JS Library 1.1.1
16739  * Copyright(c) 2006-2007, Ext JS, LLC.
16740  *
16741  * Originally Released Under LGPL - original licence link has changed is not relivant.
16742  *
16743  * Fork - LGPL
16744  * <script type="text/javascript">
16745  */
16746  
16747 /**
16748  * @class Roo.tree.TreeNode
16749  * @extends Roo.data.Node
16750  * @cfg {String} text The text for this node
16751  * @cfg {Boolean} expanded true to start the node expanded
16752  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
16753  * @cfg {Boolean} allowDrop false if this node cannot be drop on
16754  * @cfg {Boolean} disabled true to start the node disabled
16755  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
16756  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
16757  * @cfg {String} cls A css class to be added to the node
16758  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
16759  * @cfg {String} href URL of the link used for the node (defaults to #)
16760  * @cfg {String} hrefTarget target frame for the link
16761  * @cfg {String} qtip An Ext QuickTip for the node
16762  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
16763  * @cfg {Boolean} singleClickExpand True for single click expand on this node
16764  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
16765  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
16766  * (defaults to undefined with no checkbox rendered)
16767  * @constructor
16768  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
16769  */
16770 Roo.tree.TreeNode = function(attributes){
16771     attributes = attributes || {};
16772     if(typeof attributes == "string"){
16773         attributes = {text: attributes};
16774     }
16775     this.childrenRendered = false;
16776     this.rendered = false;
16777     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
16778     this.expanded = attributes.expanded === true;
16779     this.isTarget = attributes.isTarget !== false;
16780     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
16781     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
16782
16783     /**
16784      * Read-only. The text for this node. To change it use setText().
16785      * @type String
16786      */
16787     this.text = attributes.text;
16788     /**
16789      * True if this node is disabled.
16790      * @type Boolean
16791      */
16792     this.disabled = attributes.disabled === true;
16793
16794     this.addEvents({
16795         /**
16796         * @event textchange
16797         * Fires when the text for this node is changed
16798         * @param {Node} this This node
16799         * @param {String} text The new text
16800         * @param {String} oldText The old text
16801         */
16802         "textchange" : true,
16803         /**
16804         * @event beforeexpand
16805         * Fires before this node is expanded, return false to cancel.
16806         * @param {Node} this This node
16807         * @param {Boolean} deep
16808         * @param {Boolean} anim
16809         */
16810         "beforeexpand" : true,
16811         /**
16812         * @event beforecollapse
16813         * Fires before this node is collapsed, return false to cancel.
16814         * @param {Node} this This node
16815         * @param {Boolean} deep
16816         * @param {Boolean} anim
16817         */
16818         "beforecollapse" : true,
16819         /**
16820         * @event expand
16821         * Fires when this node is expanded
16822         * @param {Node} this This node
16823         */
16824         "expand" : true,
16825         /**
16826         * @event disabledchange
16827         * Fires when the disabled status of this node changes
16828         * @param {Node} this This node
16829         * @param {Boolean} disabled
16830         */
16831         "disabledchange" : true,
16832         /**
16833         * @event collapse
16834         * Fires when this node is collapsed
16835         * @param {Node} this This node
16836         */
16837         "collapse" : true,
16838         /**
16839         * @event beforeclick
16840         * Fires before click processing. Return false to cancel the default action.
16841         * @param {Node} this This node
16842         * @param {Roo.EventObject} e The event object
16843         */
16844         "beforeclick":true,
16845         /**
16846         * @event checkchange
16847         * Fires when a node with a checkbox's checked property changes
16848         * @param {Node} this This node
16849         * @param {Boolean} checked
16850         */
16851         "checkchange":true,
16852         /**
16853         * @event click
16854         * Fires when this node is clicked
16855         * @param {Node} this This node
16856         * @param {Roo.EventObject} e The event object
16857         */
16858         "click":true,
16859         /**
16860         * @event dblclick
16861         * Fires when this node is double clicked
16862         * @param {Node} this This node
16863         * @param {Roo.EventObject} e The event object
16864         */
16865         "dblclick":true,
16866         /**
16867         * @event contextmenu
16868         * Fires when this node is right clicked
16869         * @param {Node} this This node
16870         * @param {Roo.EventObject} e The event object
16871         */
16872         "contextmenu":true,
16873         /**
16874         * @event beforechildrenrendered
16875         * Fires right before the child nodes for this node are rendered
16876         * @param {Node} this This node
16877         */
16878         "beforechildrenrendered":true
16879     });
16880
16881     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
16882
16883     /**
16884      * Read-only. The UI for this node
16885      * @type TreeNodeUI
16886      */
16887     this.ui = new uiClass(this);
16888     
16889     // finally support items[]
16890     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
16891         return;
16892     }
16893     
16894     
16895     Roo.each(this.attributes.items, function(c) {
16896         this.appendChild(Roo.factory(c,Roo.Tree));
16897     }, this);
16898     delete this.attributes.items;
16899     
16900     
16901     
16902 };
16903 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
16904     preventHScroll: true,
16905     /**
16906      * Returns true if this node is expanded
16907      * @return {Boolean}
16908      */
16909     isExpanded : function(){
16910         return this.expanded;
16911     },
16912
16913     /**
16914      * Returns the UI object for this node
16915      * @return {TreeNodeUI}
16916      */
16917     getUI : function(){
16918         return this.ui;
16919     },
16920
16921     // private override
16922     setFirstChild : function(node){
16923         var of = this.firstChild;
16924         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
16925         if(this.childrenRendered && of && node != of){
16926             of.renderIndent(true, true);
16927         }
16928         if(this.rendered){
16929             this.renderIndent(true, true);
16930         }
16931     },
16932
16933     // private override
16934     setLastChild : function(node){
16935         var ol = this.lastChild;
16936         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
16937         if(this.childrenRendered && ol && node != ol){
16938             ol.renderIndent(true, true);
16939         }
16940         if(this.rendered){
16941             this.renderIndent(true, true);
16942         }
16943     },
16944
16945     // these methods are overridden to provide lazy rendering support
16946     // private override
16947     appendChild : function()
16948     {
16949         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
16950         if(node && this.childrenRendered){
16951             node.render();
16952         }
16953         this.ui.updateExpandIcon();
16954         return node;
16955     },
16956
16957     // private override
16958     removeChild : function(node){
16959         this.ownerTree.getSelectionModel().unselect(node);
16960         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
16961         // if it's been rendered remove dom node
16962         if(this.childrenRendered){
16963             node.ui.remove();
16964         }
16965         if(this.childNodes.length < 1){
16966             this.collapse(false, false);
16967         }else{
16968             this.ui.updateExpandIcon();
16969         }
16970         if(!this.firstChild) {
16971             this.childrenRendered = false;
16972         }
16973         return node;
16974     },
16975
16976     // private override
16977     insertBefore : function(node, refNode){
16978         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
16979         if(newNode && refNode && this.childrenRendered){
16980             node.render();
16981         }
16982         this.ui.updateExpandIcon();
16983         return newNode;
16984     },
16985
16986     /**
16987      * Sets the text for this node
16988      * @param {String} text
16989      */
16990     setText : function(text){
16991         var oldText = this.text;
16992         this.text = text;
16993         this.attributes.text = text;
16994         if(this.rendered){ // event without subscribing
16995             this.ui.onTextChange(this, text, oldText);
16996         }
16997         this.fireEvent("textchange", this, text, oldText);
16998     },
16999
17000     /**
17001      * Triggers selection of this node
17002      */
17003     select : function(){
17004         this.getOwnerTree().getSelectionModel().select(this);
17005     },
17006
17007     /**
17008      * Triggers deselection of this node
17009      */
17010     unselect : function(){
17011         this.getOwnerTree().getSelectionModel().unselect(this);
17012     },
17013
17014     /**
17015      * Returns true if this node is selected
17016      * @return {Boolean}
17017      */
17018     isSelected : function(){
17019         return this.getOwnerTree().getSelectionModel().isSelected(this);
17020     },
17021
17022     /**
17023      * Expand this node.
17024      * @param {Boolean} deep (optional) True to expand all children as well
17025      * @param {Boolean} anim (optional) false to cancel the default animation
17026      * @param {Function} callback (optional) A callback to be called when
17027      * expanding this node completes (does not wait for deep expand to complete).
17028      * Called with 1 parameter, this node.
17029      */
17030     expand : function(deep, anim, callback){
17031         if(!this.expanded){
17032             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17033                 return;
17034             }
17035             if(!this.childrenRendered){
17036                 this.renderChildren();
17037             }
17038             this.expanded = true;
17039             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17040                 this.ui.animExpand(function(){
17041                     this.fireEvent("expand", this);
17042                     if(typeof callback == "function"){
17043                         callback(this);
17044                     }
17045                     if(deep === true){
17046                         this.expandChildNodes(true);
17047                     }
17048                 }.createDelegate(this));
17049                 return;
17050             }else{
17051                 this.ui.expand();
17052                 this.fireEvent("expand", this);
17053                 if(typeof callback == "function"){
17054                     callback(this);
17055                 }
17056             }
17057         }else{
17058            if(typeof callback == "function"){
17059                callback(this);
17060            }
17061         }
17062         if(deep === true){
17063             this.expandChildNodes(true);
17064         }
17065     },
17066
17067     isHiddenRoot : function(){
17068         return this.isRoot && !this.getOwnerTree().rootVisible;
17069     },
17070
17071     /**
17072      * Collapse this node.
17073      * @param {Boolean} deep (optional) True to collapse all children as well
17074      * @param {Boolean} anim (optional) false to cancel the default animation
17075      */
17076     collapse : function(deep, anim){
17077         if(this.expanded && !this.isHiddenRoot()){
17078             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17079                 return;
17080             }
17081             this.expanded = false;
17082             if((this.getOwnerTree().animate && anim !== false) || anim){
17083                 this.ui.animCollapse(function(){
17084                     this.fireEvent("collapse", this);
17085                     if(deep === true){
17086                         this.collapseChildNodes(true);
17087                     }
17088                 }.createDelegate(this));
17089                 return;
17090             }else{
17091                 this.ui.collapse();
17092                 this.fireEvent("collapse", this);
17093             }
17094         }
17095         if(deep === true){
17096             var cs = this.childNodes;
17097             for(var i = 0, len = cs.length; i < len; i++) {
17098                 cs[i].collapse(true, false);
17099             }
17100         }
17101     },
17102
17103     // private
17104     delayedExpand : function(delay){
17105         if(!this.expandProcId){
17106             this.expandProcId = this.expand.defer(delay, this);
17107         }
17108     },
17109
17110     // private
17111     cancelExpand : function(){
17112         if(this.expandProcId){
17113             clearTimeout(this.expandProcId);
17114         }
17115         this.expandProcId = false;
17116     },
17117
17118     /**
17119      * Toggles expanded/collapsed state of the node
17120      */
17121     toggle : function(){
17122         if(this.expanded){
17123             this.collapse();
17124         }else{
17125             this.expand();
17126         }
17127     },
17128
17129     /**
17130      * Ensures all parent nodes are expanded
17131      */
17132     ensureVisible : function(callback){
17133         var tree = this.getOwnerTree();
17134         tree.expandPath(this.parentNode.getPath(), false, function(){
17135             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17136             Roo.callback(callback);
17137         }.createDelegate(this));
17138     },
17139
17140     /**
17141      * Expand all child nodes
17142      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17143      */
17144     expandChildNodes : function(deep){
17145         var cs = this.childNodes;
17146         for(var i = 0, len = cs.length; i < len; i++) {
17147                 cs[i].expand(deep);
17148         }
17149     },
17150
17151     /**
17152      * Collapse all child nodes
17153      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17154      */
17155     collapseChildNodes : function(deep){
17156         var cs = this.childNodes;
17157         for(var i = 0, len = cs.length; i < len; i++) {
17158                 cs[i].collapse(deep);
17159         }
17160     },
17161
17162     /**
17163      * Disables this node
17164      */
17165     disable : function(){
17166         this.disabled = true;
17167         this.unselect();
17168         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17169             this.ui.onDisableChange(this, true);
17170         }
17171         this.fireEvent("disabledchange", this, true);
17172     },
17173
17174     /**
17175      * Enables this node
17176      */
17177     enable : function(){
17178         this.disabled = false;
17179         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17180             this.ui.onDisableChange(this, false);
17181         }
17182         this.fireEvent("disabledchange", this, false);
17183     },
17184
17185     // private
17186     renderChildren : function(suppressEvent){
17187         if(suppressEvent !== false){
17188             this.fireEvent("beforechildrenrendered", this);
17189         }
17190         var cs = this.childNodes;
17191         for(var i = 0, len = cs.length; i < len; i++){
17192             cs[i].render(true);
17193         }
17194         this.childrenRendered = true;
17195     },
17196
17197     // private
17198     sort : function(fn, scope){
17199         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17200         if(this.childrenRendered){
17201             var cs = this.childNodes;
17202             for(var i = 0, len = cs.length; i < len; i++){
17203                 cs[i].render(true);
17204             }
17205         }
17206     },
17207
17208     // private
17209     render : function(bulkRender){
17210         this.ui.render(bulkRender);
17211         if(!this.rendered){
17212             this.rendered = true;
17213             if(this.expanded){
17214                 this.expanded = false;
17215                 this.expand(false, false);
17216             }
17217         }
17218     },
17219
17220     // private
17221     renderIndent : function(deep, refresh){
17222         if(refresh){
17223             this.ui.childIndent = null;
17224         }
17225         this.ui.renderIndent();
17226         if(deep === true && this.childrenRendered){
17227             var cs = this.childNodes;
17228             for(var i = 0, len = cs.length; i < len; i++){
17229                 cs[i].renderIndent(true, refresh);
17230             }
17231         }
17232     }
17233 });/*
17234  * Based on:
17235  * Ext JS Library 1.1.1
17236  * Copyright(c) 2006-2007, Ext JS, LLC.
17237  *
17238  * Originally Released Under LGPL - original licence link has changed is not relivant.
17239  *
17240  * Fork - LGPL
17241  * <script type="text/javascript">
17242  */
17243  
17244 /**
17245  * @class Roo.tree.AsyncTreeNode
17246  * @extends Roo.tree.TreeNode
17247  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17248  * @constructor
17249  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17250  */
17251  Roo.tree.AsyncTreeNode = function(config){
17252     this.loaded = false;
17253     this.loading = false;
17254     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17255     /**
17256     * @event beforeload
17257     * Fires before this node is loaded, return false to cancel
17258     * @param {Node} this This node
17259     */
17260     this.addEvents({'beforeload':true, 'load': true});
17261     /**
17262     * @event load
17263     * Fires when this node is loaded
17264     * @param {Node} this This node
17265     */
17266     /**
17267      * The loader used by this node (defaults to using the tree's defined loader)
17268      * @type TreeLoader
17269      * @property loader
17270      */
17271 };
17272 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17273     expand : function(deep, anim, callback){
17274         if(this.loading){ // if an async load is already running, waiting til it's done
17275             var timer;
17276             var f = function(){
17277                 if(!this.loading){ // done loading
17278                     clearInterval(timer);
17279                     this.expand(deep, anim, callback);
17280                 }
17281             }.createDelegate(this);
17282             timer = setInterval(f, 200);
17283             return;
17284         }
17285         if(!this.loaded){
17286             if(this.fireEvent("beforeload", this) === false){
17287                 return;
17288             }
17289             this.loading = true;
17290             this.ui.beforeLoad(this);
17291             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17292             if(loader){
17293                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17294                 return;
17295             }
17296         }
17297         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17298     },
17299     
17300     /**
17301      * Returns true if this node is currently loading
17302      * @return {Boolean}
17303      */
17304     isLoading : function(){
17305         return this.loading;  
17306     },
17307     
17308     loadComplete : function(deep, anim, callback){
17309         this.loading = false;
17310         this.loaded = true;
17311         this.ui.afterLoad(this);
17312         this.fireEvent("load", this);
17313         this.expand(deep, anim, callback);
17314     },
17315     
17316     /**
17317      * Returns true if this node has been loaded
17318      * @return {Boolean}
17319      */
17320     isLoaded : function(){
17321         return this.loaded;
17322     },
17323     
17324     hasChildNodes : function(){
17325         if(!this.isLeaf() && !this.loaded){
17326             return true;
17327         }else{
17328             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17329         }
17330     },
17331
17332     /**
17333      * Trigger a reload for this node
17334      * @param {Function} callback
17335      */
17336     reload : function(callback){
17337         this.collapse(false, false);
17338         while(this.firstChild){
17339             this.removeChild(this.firstChild);
17340         }
17341         this.childrenRendered = false;
17342         this.loaded = false;
17343         if(this.isHiddenRoot()){
17344             this.expanded = false;
17345         }
17346         this.expand(false, false, callback);
17347     }
17348 });/*
17349  * Based on:
17350  * Ext JS Library 1.1.1
17351  * Copyright(c) 2006-2007, Ext JS, LLC.
17352  *
17353  * Originally Released Under LGPL - original licence link has changed is not relivant.
17354  *
17355  * Fork - LGPL
17356  * <script type="text/javascript">
17357  */
17358  
17359 /**
17360  * @class Roo.tree.TreeNodeUI
17361  * @constructor
17362  * @param {Object} node The node to render
17363  * The TreeNode UI implementation is separate from the
17364  * tree implementation. Unless you are customizing the tree UI,
17365  * you should never have to use this directly.
17366  */
17367 Roo.tree.TreeNodeUI = function(node){
17368     this.node = node;
17369     this.rendered = false;
17370     this.animating = false;
17371     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17372 };
17373
17374 Roo.tree.TreeNodeUI.prototype = {
17375     removeChild : function(node){
17376         if(this.rendered){
17377             this.ctNode.removeChild(node.ui.getEl());
17378         }
17379     },
17380
17381     beforeLoad : function(){
17382          this.addClass("x-tree-node-loading");
17383     },
17384
17385     afterLoad : function(){
17386          this.removeClass("x-tree-node-loading");
17387     },
17388
17389     onTextChange : function(node, text, oldText){
17390         if(this.rendered){
17391             this.textNode.innerHTML = text;
17392         }
17393     },
17394
17395     onDisableChange : function(node, state){
17396         this.disabled = state;
17397         if(state){
17398             this.addClass("x-tree-node-disabled");
17399         }else{
17400             this.removeClass("x-tree-node-disabled");
17401         }
17402     },
17403
17404     onSelectedChange : function(state){
17405         if(state){
17406             this.focus();
17407             this.addClass("x-tree-selected");
17408         }else{
17409             //this.blur();
17410             this.removeClass("x-tree-selected");
17411         }
17412     },
17413
17414     onMove : function(tree, node, oldParent, newParent, index, refNode){
17415         this.childIndent = null;
17416         if(this.rendered){
17417             var targetNode = newParent.ui.getContainer();
17418             if(!targetNode){//target not rendered
17419                 this.holder = document.createElement("div");
17420                 this.holder.appendChild(this.wrap);
17421                 return;
17422             }
17423             var insertBefore = refNode ? refNode.ui.getEl() : null;
17424             if(insertBefore){
17425                 targetNode.insertBefore(this.wrap, insertBefore);
17426             }else{
17427                 targetNode.appendChild(this.wrap);
17428             }
17429             this.node.renderIndent(true);
17430         }
17431     },
17432
17433     addClass : function(cls){
17434         if(this.elNode){
17435             Roo.fly(this.elNode).addClass(cls);
17436         }
17437     },
17438
17439     removeClass : function(cls){
17440         if(this.elNode){
17441             Roo.fly(this.elNode).removeClass(cls);
17442         }
17443     },
17444
17445     remove : function(){
17446         if(this.rendered){
17447             this.holder = document.createElement("div");
17448             this.holder.appendChild(this.wrap);
17449         }
17450     },
17451
17452     fireEvent : function(){
17453         return this.node.fireEvent.apply(this.node, arguments);
17454     },
17455
17456     initEvents : function(){
17457         this.node.on("move", this.onMove, this);
17458         var E = Roo.EventManager;
17459         var a = this.anchor;
17460
17461         var el = Roo.fly(a, '_treeui');
17462
17463         if(Roo.isOpera){ // opera render bug ignores the CSS
17464             el.setStyle("text-decoration", "none");
17465         }
17466
17467         el.on("click", this.onClick, this);
17468         el.on("dblclick", this.onDblClick, this);
17469
17470         if(this.checkbox){
17471             Roo.EventManager.on(this.checkbox,
17472                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17473         }
17474
17475         el.on("contextmenu", this.onContextMenu, this);
17476
17477         var icon = Roo.fly(this.iconNode);
17478         icon.on("click", this.onClick, this);
17479         icon.on("dblclick", this.onDblClick, this);
17480         icon.on("contextmenu", this.onContextMenu, this);
17481         E.on(this.ecNode, "click", this.ecClick, this, true);
17482
17483         if(this.node.disabled){
17484             this.addClass("x-tree-node-disabled");
17485         }
17486         if(this.node.hidden){
17487             this.addClass("x-tree-node-disabled");
17488         }
17489         var ot = this.node.getOwnerTree();
17490         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17491         if(dd && (!this.node.isRoot || ot.rootVisible)){
17492             Roo.dd.Registry.register(this.elNode, {
17493                 node: this.node,
17494                 handles: this.getDDHandles(),
17495                 isHandle: false
17496             });
17497         }
17498     },
17499
17500     getDDHandles : function(){
17501         return [this.iconNode, this.textNode];
17502     },
17503
17504     hide : function(){
17505         if(this.rendered){
17506             this.wrap.style.display = "none";
17507         }
17508     },
17509
17510     show : function(){
17511         if(this.rendered){
17512             this.wrap.style.display = "";
17513         }
17514     },
17515
17516     onContextMenu : function(e){
17517         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17518             e.preventDefault();
17519             this.focus();
17520             this.fireEvent("contextmenu", this.node, e);
17521         }
17522     },
17523
17524     onClick : function(e){
17525         if(this.dropping){
17526             e.stopEvent();
17527             return;
17528         }
17529         if(this.fireEvent("beforeclick", this.node, e) !== false){
17530             if(!this.disabled && this.node.attributes.href){
17531                 this.fireEvent("click", this.node, e);
17532                 return;
17533             }
17534             e.preventDefault();
17535             if(this.disabled){
17536                 return;
17537             }
17538
17539             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17540                 this.node.toggle();
17541             }
17542
17543             this.fireEvent("click", this.node, e);
17544         }else{
17545             e.stopEvent();
17546         }
17547     },
17548
17549     onDblClick : function(e){
17550         e.preventDefault();
17551         if(this.disabled){
17552             return;
17553         }
17554         if(this.checkbox){
17555             this.toggleCheck();
17556         }
17557         if(!this.animating && this.node.hasChildNodes()){
17558             this.node.toggle();
17559         }
17560         this.fireEvent("dblclick", this.node, e);
17561     },
17562
17563     onCheckChange : function(){
17564         var checked = this.checkbox.checked;
17565         this.node.attributes.checked = checked;
17566         this.fireEvent('checkchange', this.node, checked);
17567     },
17568
17569     ecClick : function(e){
17570         if(!this.animating && this.node.hasChildNodes()){
17571             this.node.toggle();
17572         }
17573     },
17574
17575     startDrop : function(){
17576         this.dropping = true;
17577     },
17578
17579     // delayed drop so the click event doesn't get fired on a drop
17580     endDrop : function(){
17581        setTimeout(function(){
17582            this.dropping = false;
17583        }.createDelegate(this), 50);
17584     },
17585
17586     expand : function(){
17587         this.updateExpandIcon();
17588         this.ctNode.style.display = "";
17589     },
17590
17591     focus : function(){
17592         if(!this.node.preventHScroll){
17593             try{this.anchor.focus();
17594             }catch(e){}
17595         }else if(!Roo.isIE){
17596             try{
17597                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17598                 var l = noscroll.scrollLeft;
17599                 this.anchor.focus();
17600                 noscroll.scrollLeft = l;
17601             }catch(e){}
17602         }
17603     },
17604
17605     toggleCheck : function(value){
17606         var cb = this.checkbox;
17607         if(cb){
17608             cb.checked = (value === undefined ? !cb.checked : value);
17609         }
17610     },
17611
17612     blur : function(){
17613         try{
17614             this.anchor.blur();
17615         }catch(e){}
17616     },
17617
17618     animExpand : function(callback){
17619         var ct = Roo.get(this.ctNode);
17620         ct.stopFx();
17621         if(!this.node.hasChildNodes()){
17622             this.updateExpandIcon();
17623             this.ctNode.style.display = "";
17624             Roo.callback(callback);
17625             return;
17626         }
17627         this.animating = true;
17628         this.updateExpandIcon();
17629
17630         ct.slideIn('t', {
17631            callback : function(){
17632                this.animating = false;
17633                Roo.callback(callback);
17634             },
17635             scope: this,
17636             duration: this.node.ownerTree.duration || .25
17637         });
17638     },
17639
17640     highlight : function(){
17641         var tree = this.node.getOwnerTree();
17642         Roo.fly(this.wrap).highlight(
17643             tree.hlColor || "C3DAF9",
17644             {endColor: tree.hlBaseColor}
17645         );
17646     },
17647
17648     collapse : function(){
17649         this.updateExpandIcon();
17650         this.ctNode.style.display = "none";
17651     },
17652
17653     animCollapse : function(callback){
17654         var ct = Roo.get(this.ctNode);
17655         ct.enableDisplayMode('block');
17656         ct.stopFx();
17657
17658         this.animating = true;
17659         this.updateExpandIcon();
17660
17661         ct.slideOut('t', {
17662             callback : function(){
17663                this.animating = false;
17664                Roo.callback(callback);
17665             },
17666             scope: this,
17667             duration: this.node.ownerTree.duration || .25
17668         });
17669     },
17670
17671     getContainer : function(){
17672         return this.ctNode;
17673     },
17674
17675     getEl : function(){
17676         return this.wrap;
17677     },
17678
17679     appendDDGhost : function(ghostNode){
17680         ghostNode.appendChild(this.elNode.cloneNode(true));
17681     },
17682
17683     getDDRepairXY : function(){
17684         return Roo.lib.Dom.getXY(this.iconNode);
17685     },
17686
17687     onRender : function(){
17688         this.render();
17689     },
17690
17691     render : function(bulkRender){
17692         var n = this.node, a = n.attributes;
17693         var targetNode = n.parentNode ?
17694               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
17695
17696         if(!this.rendered){
17697             this.rendered = true;
17698
17699             this.renderElements(n, a, targetNode, bulkRender);
17700
17701             if(a.qtip){
17702                if(this.textNode.setAttributeNS){
17703                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
17704                    if(a.qtipTitle){
17705                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
17706                    }
17707                }else{
17708                    this.textNode.setAttribute("ext:qtip", a.qtip);
17709                    if(a.qtipTitle){
17710                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
17711                    }
17712                }
17713             }else if(a.qtipCfg){
17714                 a.qtipCfg.target = Roo.id(this.textNode);
17715                 Roo.QuickTips.register(a.qtipCfg);
17716             }
17717             this.initEvents();
17718             if(!this.node.expanded){
17719                 this.updateExpandIcon();
17720             }
17721         }else{
17722             if(bulkRender === true) {
17723                 targetNode.appendChild(this.wrap);
17724             }
17725         }
17726     },
17727
17728     renderElements : function(n, a, targetNode, bulkRender)
17729     {
17730         // add some indent caching, this helps performance when rendering a large tree
17731         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
17732         var t = n.getOwnerTree();
17733         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
17734         if (typeof(n.attributes.html) != 'undefined') {
17735             txt = n.attributes.html;
17736         }
17737         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
17738         var cb = typeof a.checked == 'boolean';
17739         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
17740         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
17741             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
17742             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
17743             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
17744             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
17745             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
17746              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
17747                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
17748             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
17749             "</li>"];
17750
17751         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
17752             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
17753                                 n.nextSibling.ui.getEl(), buf.join(""));
17754         }else{
17755             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
17756         }
17757
17758         this.elNode = this.wrap.childNodes[0];
17759         this.ctNode = this.wrap.childNodes[1];
17760         var cs = this.elNode.childNodes;
17761         this.indentNode = cs[0];
17762         this.ecNode = cs[1];
17763         this.iconNode = cs[2];
17764         var index = 3;
17765         if(cb){
17766             this.checkbox = cs[3];
17767             index++;
17768         }
17769         this.anchor = cs[index];
17770         this.textNode = cs[index].firstChild;
17771     },
17772
17773     getAnchor : function(){
17774         return this.anchor;
17775     },
17776
17777     getTextEl : function(){
17778         return this.textNode;
17779     },
17780
17781     getIconEl : function(){
17782         return this.iconNode;
17783     },
17784
17785     isChecked : function(){
17786         return this.checkbox ? this.checkbox.checked : false;
17787     },
17788
17789     updateExpandIcon : function(){
17790         if(this.rendered){
17791             var n = this.node, c1, c2;
17792             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
17793             var hasChild = n.hasChildNodes();
17794             if(hasChild){
17795                 if(n.expanded){
17796                     cls += "-minus";
17797                     c1 = "x-tree-node-collapsed";
17798                     c2 = "x-tree-node-expanded";
17799                 }else{
17800                     cls += "-plus";
17801                     c1 = "x-tree-node-expanded";
17802                     c2 = "x-tree-node-collapsed";
17803                 }
17804                 if(this.wasLeaf){
17805                     this.removeClass("x-tree-node-leaf");
17806                     this.wasLeaf = false;
17807                 }
17808                 if(this.c1 != c1 || this.c2 != c2){
17809                     Roo.fly(this.elNode).replaceClass(c1, c2);
17810                     this.c1 = c1; this.c2 = c2;
17811                 }
17812             }else{
17813                 // this changes non-leafs into leafs if they have no children.
17814                 // it's not very rational behaviour..
17815                 
17816                 if(!this.wasLeaf && this.node.leaf){
17817                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
17818                     delete this.c1;
17819                     delete this.c2;
17820                     this.wasLeaf = true;
17821                 }
17822             }
17823             var ecc = "x-tree-ec-icon "+cls;
17824             if(this.ecc != ecc){
17825                 this.ecNode.className = ecc;
17826                 this.ecc = ecc;
17827             }
17828         }
17829     },
17830
17831     getChildIndent : function(){
17832         if(!this.childIndent){
17833             var buf = [];
17834             var p = this.node;
17835             while(p){
17836                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
17837                     if(!p.isLast()) {
17838                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
17839                     } else {
17840                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
17841                     }
17842                 }
17843                 p = p.parentNode;
17844             }
17845             this.childIndent = buf.join("");
17846         }
17847         return this.childIndent;
17848     },
17849
17850     renderIndent : function(){
17851         if(this.rendered){
17852             var indent = "";
17853             var p = this.node.parentNode;
17854             if(p){
17855                 indent = p.ui.getChildIndent();
17856             }
17857             if(this.indentMarkup != indent){ // don't rerender if not required
17858                 this.indentNode.innerHTML = indent;
17859                 this.indentMarkup = indent;
17860             }
17861             this.updateExpandIcon();
17862         }
17863     }
17864 };
17865
17866 Roo.tree.RootTreeNodeUI = function(){
17867     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
17868 };
17869 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
17870     render : function(){
17871         if(!this.rendered){
17872             var targetNode = this.node.ownerTree.innerCt.dom;
17873             this.node.expanded = true;
17874             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
17875             this.wrap = this.ctNode = targetNode.firstChild;
17876         }
17877     },
17878     collapse : function(){
17879     },
17880     expand : function(){
17881     }
17882 });/*
17883  * Based on:
17884  * Ext JS Library 1.1.1
17885  * Copyright(c) 2006-2007, Ext JS, LLC.
17886  *
17887  * Originally Released Under LGPL - original licence link has changed is not relivant.
17888  *
17889  * Fork - LGPL
17890  * <script type="text/javascript">
17891  */
17892 /**
17893  * @class Roo.tree.TreeLoader
17894  * @extends Roo.util.Observable
17895  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
17896  * nodes from a specified URL. The response must be a javascript Array definition
17897  * who's elements are node definition objects. eg:
17898  * <pre><code>
17899 {  success : true,
17900    data :      [
17901    
17902     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
17903     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
17904     ]
17905 }
17906
17907
17908 </code></pre>
17909  * <br><br>
17910  * The old style respose with just an array is still supported, but not recommended.
17911  * <br><br>
17912  *
17913  * A server request is sent, and child nodes are loaded only when a node is expanded.
17914  * The loading node's id is passed to the server under the parameter name "node" to
17915  * enable the server to produce the correct child nodes.
17916  * <br><br>
17917  * To pass extra parameters, an event handler may be attached to the "beforeload"
17918  * event, and the parameters specified in the TreeLoader's baseParams property:
17919  * <pre><code>
17920     myTreeLoader.on("beforeload", function(treeLoader, node) {
17921         this.baseParams.category = node.attributes.category;
17922     }, this);
17923 </code></pre><
17924  * This would pass an HTTP parameter called "category" to the server containing
17925  * the value of the Node's "category" attribute.
17926  * @constructor
17927  * Creates a new Treeloader.
17928  * @param {Object} config A config object containing config properties.
17929  */
17930 Roo.tree.TreeLoader = function(config){
17931     this.baseParams = {};
17932     this.requestMethod = "POST";
17933     Roo.apply(this, config);
17934
17935     this.addEvents({
17936     
17937         /**
17938          * @event beforeload
17939          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
17940          * @param {Object} This TreeLoader object.
17941          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17942          * @param {Object} callback The callback function specified in the {@link #load} call.
17943          */
17944         beforeload : true,
17945         /**
17946          * @event load
17947          * Fires when the node has been successfuly loaded.
17948          * @param {Object} This TreeLoader object.
17949          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17950          * @param {Object} response The response object containing the data from the server.
17951          */
17952         load : true,
17953         /**
17954          * @event loadexception
17955          * Fires if the network request failed.
17956          * @param {Object} This TreeLoader object.
17957          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17958          * @param {Object} response The response object containing the data from the server.
17959          */
17960         loadexception : true,
17961         /**
17962          * @event create
17963          * Fires before a node is created, enabling you to return custom Node types 
17964          * @param {Object} This TreeLoader object.
17965          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
17966          */
17967         create : true
17968     });
17969
17970     Roo.tree.TreeLoader.superclass.constructor.call(this);
17971 };
17972
17973 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
17974     /**
17975     * @cfg {String} dataUrl The URL from which to request a Json string which
17976     * specifies an array of node definition object representing the child nodes
17977     * to be loaded.
17978     */
17979     /**
17980     * @cfg {String} requestMethod either GET or POST
17981     * defaults to POST (due to BC)
17982     * to be loaded.
17983     */
17984     /**
17985     * @cfg {Object} baseParams (optional) An object containing properties which
17986     * specify HTTP parameters to be passed to each request for child nodes.
17987     */
17988     /**
17989     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
17990     * created by this loader. If the attributes sent by the server have an attribute in this object,
17991     * they take priority.
17992     */
17993     /**
17994     * @cfg {Object} uiProviders (optional) An object containing properties which
17995     * 
17996     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
17997     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
17998     * <i>uiProvider</i> attribute of a returned child node is a string rather
17999     * than a reference to a TreeNodeUI implementation, this that string value
18000     * is used as a property name in the uiProviders object. You can define the provider named
18001     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18002     */
18003     uiProviders : {},
18004
18005     /**
18006     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18007     * child nodes before loading.
18008     */
18009     clearOnLoad : true,
18010
18011     /**
18012     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18013     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18014     * Grid query { data : [ .....] }
18015     */
18016     
18017     root : false,
18018      /**
18019     * @cfg {String} queryParam (optional) 
18020     * Name of the query as it will be passed on the querystring (defaults to 'node')
18021     * eg. the request will be ?node=[id]
18022     */
18023     
18024     
18025     queryParam: false,
18026     
18027     /**
18028      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18029      * This is called automatically when a node is expanded, but may be used to reload
18030      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18031      * @param {Roo.tree.TreeNode} node
18032      * @param {Function} callback
18033      */
18034     load : function(node, callback){
18035         if(this.clearOnLoad){
18036             while(node.firstChild){
18037                 node.removeChild(node.firstChild);
18038             }
18039         }
18040         if(node.attributes.children){ // preloaded json children
18041             var cs = node.attributes.children;
18042             for(var i = 0, len = cs.length; i < len; i++){
18043                 node.appendChild(this.createNode(cs[i]));
18044             }
18045             if(typeof callback == "function"){
18046                 callback();
18047             }
18048         }else if(this.dataUrl){
18049             this.requestData(node, callback);
18050         }
18051     },
18052
18053     getParams: function(node){
18054         var buf = [], bp = this.baseParams;
18055         for(var key in bp){
18056             if(typeof bp[key] != "function"){
18057                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18058             }
18059         }
18060         var n = this.queryParam === false ? 'node' : this.queryParam;
18061         buf.push(n + "=", encodeURIComponent(node.id));
18062         return buf.join("");
18063     },
18064
18065     requestData : function(node, callback){
18066         if(this.fireEvent("beforeload", this, node, callback) !== false){
18067             this.transId = Roo.Ajax.request({
18068                 method:this.requestMethod,
18069                 url: this.dataUrl||this.url,
18070                 success: this.handleResponse,
18071                 failure: this.handleFailure,
18072                 scope: this,
18073                 argument: {callback: callback, node: node},
18074                 params: this.getParams(node)
18075             });
18076         }else{
18077             // if the load is cancelled, make sure we notify
18078             // the node that we are done
18079             if(typeof callback == "function"){
18080                 callback();
18081             }
18082         }
18083     },
18084
18085     isLoading : function(){
18086         return this.transId ? true : false;
18087     },
18088
18089     abort : function(){
18090         if(this.isLoading()){
18091             Roo.Ajax.abort(this.transId);
18092         }
18093     },
18094
18095     // private
18096     createNode : function(attr)
18097     {
18098         // apply baseAttrs, nice idea Corey!
18099         if(this.baseAttrs){
18100             Roo.applyIf(attr, this.baseAttrs);
18101         }
18102         if(this.applyLoader !== false){
18103             attr.loader = this;
18104         }
18105         // uiProvider = depreciated..
18106         
18107         if(typeof(attr.uiProvider) == 'string'){
18108            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18109                 /**  eval:var:attr */ eval(attr.uiProvider);
18110         }
18111         if(typeof(this.uiProviders['default']) != 'undefined') {
18112             attr.uiProvider = this.uiProviders['default'];
18113         }
18114         
18115         this.fireEvent('create', this, attr);
18116         
18117         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18118         return(attr.leaf ?
18119                         new Roo.tree.TreeNode(attr) :
18120                         new Roo.tree.AsyncTreeNode(attr));
18121     },
18122
18123     processResponse : function(response, node, callback)
18124     {
18125         var json = response.responseText;
18126         try {
18127             
18128             var o = Roo.decode(json);
18129             
18130             if (this.root === false && typeof(o.success) != undefined) {
18131                 this.root = 'data'; // the default behaviour for list like data..
18132                 }
18133                 
18134             if (this.root !== false &&  !o.success) {
18135                 // it's a failure condition.
18136                 var a = response.argument;
18137                 this.fireEvent("loadexception", this, a.node, response);
18138                 Roo.log("Load failed - should have a handler really");
18139                 return;
18140             }
18141             
18142             
18143             
18144             if (this.root !== false) {
18145                  o = o[this.root];
18146             }
18147             
18148             for(var i = 0, len = o.length; i < len; i++){
18149                 var n = this.createNode(o[i]);
18150                 if(n){
18151                     node.appendChild(n);
18152                 }
18153             }
18154             if(typeof callback == "function"){
18155                 callback(this, node);
18156             }
18157         }catch(e){
18158             this.handleFailure(response);
18159         }
18160     },
18161
18162     handleResponse : function(response){
18163         this.transId = false;
18164         var a = response.argument;
18165         this.processResponse(response, a.node, a.callback);
18166         this.fireEvent("load", this, a.node, response);
18167     },
18168
18169     handleFailure : function(response)
18170     {
18171         // should handle failure better..
18172         this.transId = false;
18173         var a = response.argument;
18174         this.fireEvent("loadexception", this, a.node, response);
18175         if(typeof a.callback == "function"){
18176             a.callback(this, a.node);
18177         }
18178     }
18179 });/*
18180  * Based on:
18181  * Ext JS Library 1.1.1
18182  * Copyright(c) 2006-2007, Ext JS, LLC.
18183  *
18184  * Originally Released Under LGPL - original licence link has changed is not relivant.
18185  *
18186  * Fork - LGPL
18187  * <script type="text/javascript">
18188  */
18189
18190 /**
18191 * @class Roo.tree.TreeFilter
18192 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18193 * @param {TreePanel} tree
18194 * @param {Object} config (optional)
18195  */
18196 Roo.tree.TreeFilter = function(tree, config){
18197     this.tree = tree;
18198     this.filtered = {};
18199     Roo.apply(this, config);
18200 };
18201
18202 Roo.tree.TreeFilter.prototype = {
18203     clearBlank:false,
18204     reverse:false,
18205     autoClear:false,
18206     remove:false,
18207
18208      /**
18209      * Filter the data by a specific attribute.
18210      * @param {String/RegExp} value Either string that the attribute value
18211      * should start with or a RegExp to test against the attribute
18212      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18213      * @param {TreeNode} startNode (optional) The node to start the filter at.
18214      */
18215     filter : function(value, attr, startNode){
18216         attr = attr || "text";
18217         var f;
18218         if(typeof value == "string"){
18219             var vlen = value.length;
18220             // auto clear empty filter
18221             if(vlen == 0 && this.clearBlank){
18222                 this.clear();
18223                 return;
18224             }
18225             value = value.toLowerCase();
18226             f = function(n){
18227                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18228             };
18229         }else if(value.exec){ // regex?
18230             f = function(n){
18231                 return value.test(n.attributes[attr]);
18232             };
18233         }else{
18234             throw 'Illegal filter type, must be string or regex';
18235         }
18236         this.filterBy(f, null, startNode);
18237         },
18238
18239     /**
18240      * Filter by a function. The passed function will be called with each
18241      * node in the tree (or from the startNode). If the function returns true, the node is kept
18242      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18243      * @param {Function} fn The filter function
18244      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18245      */
18246     filterBy : function(fn, scope, startNode){
18247         startNode = startNode || this.tree.root;
18248         if(this.autoClear){
18249             this.clear();
18250         }
18251         var af = this.filtered, rv = this.reverse;
18252         var f = function(n){
18253             if(n == startNode){
18254                 return true;
18255             }
18256             if(af[n.id]){
18257                 return false;
18258             }
18259             var m = fn.call(scope || n, n);
18260             if(!m || rv){
18261                 af[n.id] = n;
18262                 n.ui.hide();
18263                 return false;
18264             }
18265             return true;
18266         };
18267         startNode.cascade(f);
18268         if(this.remove){
18269            for(var id in af){
18270                if(typeof id != "function"){
18271                    var n = af[id];
18272                    if(n && n.parentNode){
18273                        n.parentNode.removeChild(n);
18274                    }
18275                }
18276            }
18277         }
18278     },
18279
18280     /**
18281      * Clears the current filter. Note: with the "remove" option
18282      * set a filter cannot be cleared.
18283      */
18284     clear : function(){
18285         var t = this.tree;
18286         var af = this.filtered;
18287         for(var id in af){
18288             if(typeof id != "function"){
18289                 var n = af[id];
18290                 if(n){
18291                     n.ui.show();
18292                 }
18293             }
18294         }
18295         this.filtered = {};
18296     }
18297 };
18298 /*
18299  * Based on:
18300  * Ext JS Library 1.1.1
18301  * Copyright(c) 2006-2007, Ext JS, LLC.
18302  *
18303  * Originally Released Under LGPL - original licence link has changed is not relivant.
18304  *
18305  * Fork - LGPL
18306  * <script type="text/javascript">
18307  */
18308  
18309
18310 /**
18311  * @class Roo.tree.TreeSorter
18312  * Provides sorting of nodes in a TreePanel
18313  * 
18314  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18315  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18316  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18317  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18318  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18319  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18320  * @constructor
18321  * @param {TreePanel} tree
18322  * @param {Object} config
18323  */
18324 Roo.tree.TreeSorter = function(tree, config){
18325     Roo.apply(this, config);
18326     tree.on("beforechildrenrendered", this.doSort, this);
18327     tree.on("append", this.updateSort, this);
18328     tree.on("insert", this.updateSort, this);
18329     
18330     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18331     var p = this.property || "text";
18332     var sortType = this.sortType;
18333     var fs = this.folderSort;
18334     var cs = this.caseSensitive === true;
18335     var leafAttr = this.leafAttr || 'leaf';
18336
18337     this.sortFn = function(n1, n2){
18338         if(fs){
18339             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18340                 return 1;
18341             }
18342             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18343                 return -1;
18344             }
18345         }
18346         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18347         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18348         if(v1 < v2){
18349                         return dsc ? +1 : -1;
18350                 }else if(v1 > v2){
18351                         return dsc ? -1 : +1;
18352         }else{
18353                 return 0;
18354         }
18355     };
18356 };
18357
18358 Roo.tree.TreeSorter.prototype = {
18359     doSort : function(node){
18360         node.sort(this.sortFn);
18361     },
18362     
18363     compareNodes : function(n1, n2){
18364         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18365     },
18366     
18367     updateSort : function(tree, node){
18368         if(node.childrenRendered){
18369             this.doSort.defer(1, this, [node]);
18370         }
18371     }
18372 };/*
18373  * Based on:
18374  * Ext JS Library 1.1.1
18375  * Copyright(c) 2006-2007, Ext JS, LLC.
18376  *
18377  * Originally Released Under LGPL - original licence link has changed is not relivant.
18378  *
18379  * Fork - LGPL
18380  * <script type="text/javascript">
18381  */
18382
18383 if(Roo.dd.DropZone){
18384     
18385 Roo.tree.TreeDropZone = function(tree, config){
18386     this.allowParentInsert = false;
18387     this.allowContainerDrop = false;
18388     this.appendOnly = false;
18389     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18390     this.tree = tree;
18391     this.lastInsertClass = "x-tree-no-status";
18392     this.dragOverData = {};
18393 };
18394
18395 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18396     ddGroup : "TreeDD",
18397     scroll:  true,
18398     
18399     expandDelay : 1000,
18400     
18401     expandNode : function(node){
18402         if(node.hasChildNodes() && !node.isExpanded()){
18403             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18404         }
18405     },
18406     
18407     queueExpand : function(node){
18408         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18409     },
18410     
18411     cancelExpand : function(){
18412         if(this.expandProcId){
18413             clearTimeout(this.expandProcId);
18414             this.expandProcId = false;
18415         }
18416     },
18417     
18418     isValidDropPoint : function(n, pt, dd, e, data){
18419         if(!n || !data){ return false; }
18420         var targetNode = n.node;
18421         var dropNode = data.node;
18422         // default drop rules
18423         if(!(targetNode && targetNode.isTarget && pt)){
18424             return false;
18425         }
18426         if(pt == "append" && targetNode.allowChildren === false){
18427             return false;
18428         }
18429         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18430             return false;
18431         }
18432         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18433             return false;
18434         }
18435         // reuse the object
18436         var overEvent = this.dragOverData;
18437         overEvent.tree = this.tree;
18438         overEvent.target = targetNode;
18439         overEvent.data = data;
18440         overEvent.point = pt;
18441         overEvent.source = dd;
18442         overEvent.rawEvent = e;
18443         overEvent.dropNode = dropNode;
18444         overEvent.cancel = false;  
18445         var result = this.tree.fireEvent("nodedragover", overEvent);
18446         return overEvent.cancel === false && result !== false;
18447     },
18448     
18449     getDropPoint : function(e, n, dd)
18450     {
18451         var tn = n.node;
18452         if(tn.isRoot){
18453             return tn.allowChildren !== false ? "append" : false; // always append for root
18454         }
18455         var dragEl = n.ddel;
18456         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18457         var y = Roo.lib.Event.getPageY(e);
18458         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18459         
18460         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18461         var noAppend = tn.allowChildren === false;
18462         if(this.appendOnly || tn.parentNode.allowChildren === false){
18463             return noAppend ? false : "append";
18464         }
18465         var noBelow = false;
18466         if(!this.allowParentInsert){
18467             noBelow = tn.hasChildNodes() && tn.isExpanded();
18468         }
18469         var q = (b - t) / (noAppend ? 2 : 3);
18470         if(y >= t && y < (t + q)){
18471             return "above";
18472         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18473             return "below";
18474         }else{
18475             return "append";
18476         }
18477     },
18478     
18479     onNodeEnter : function(n, dd, e, data)
18480     {
18481         this.cancelExpand();
18482     },
18483     
18484     onNodeOver : function(n, dd, e, data)
18485     {
18486        
18487         var pt = this.getDropPoint(e, n, dd);
18488         var node = n.node;
18489         
18490         // auto node expand check
18491         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18492             this.queueExpand(node);
18493         }else if(pt != "append"){
18494             this.cancelExpand();
18495         }
18496         
18497         // set the insert point style on the target node
18498         var returnCls = this.dropNotAllowed;
18499         if(this.isValidDropPoint(n, pt, dd, e, data)){
18500            if(pt){
18501                var el = n.ddel;
18502                var cls;
18503                if(pt == "above"){
18504                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18505                    cls = "x-tree-drag-insert-above";
18506                }else if(pt == "below"){
18507                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18508                    cls = "x-tree-drag-insert-below";
18509                }else{
18510                    returnCls = "x-tree-drop-ok-append";
18511                    cls = "x-tree-drag-append";
18512                }
18513                if(this.lastInsertClass != cls){
18514                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18515                    this.lastInsertClass = cls;
18516                }
18517            }
18518        }
18519        return returnCls;
18520     },
18521     
18522     onNodeOut : function(n, dd, e, data){
18523         
18524         this.cancelExpand();
18525         this.removeDropIndicators(n);
18526     },
18527     
18528     onNodeDrop : function(n, dd, e, data){
18529         var point = this.getDropPoint(e, n, dd);
18530         var targetNode = n.node;
18531         targetNode.ui.startDrop();
18532         if(!this.isValidDropPoint(n, point, dd, e, data)){
18533             targetNode.ui.endDrop();
18534             return false;
18535         }
18536         // first try to find the drop node
18537         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18538         var dropEvent = {
18539             tree : this.tree,
18540             target: targetNode,
18541             data: data,
18542             point: point,
18543             source: dd,
18544             rawEvent: e,
18545             dropNode: dropNode,
18546             cancel: !dropNode   
18547         };
18548         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18549         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18550             targetNode.ui.endDrop();
18551             return false;
18552         }
18553         // allow target changing
18554         targetNode = dropEvent.target;
18555         if(point == "append" && !targetNode.isExpanded()){
18556             targetNode.expand(false, null, function(){
18557                 this.completeDrop(dropEvent);
18558             }.createDelegate(this));
18559         }else{
18560             this.completeDrop(dropEvent);
18561         }
18562         return true;
18563     },
18564     
18565     completeDrop : function(de){
18566         var ns = de.dropNode, p = de.point, t = de.target;
18567         if(!(ns instanceof Array)){
18568             ns = [ns];
18569         }
18570         var n;
18571         for(var i = 0, len = ns.length; i < len; i++){
18572             n = ns[i];
18573             if(p == "above"){
18574                 t.parentNode.insertBefore(n, t);
18575             }else if(p == "below"){
18576                 t.parentNode.insertBefore(n, t.nextSibling);
18577             }else{
18578                 t.appendChild(n);
18579             }
18580         }
18581         n.ui.focus();
18582         if(this.tree.hlDrop){
18583             n.ui.highlight();
18584         }
18585         t.ui.endDrop();
18586         this.tree.fireEvent("nodedrop", de);
18587     },
18588     
18589     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18590         if(this.tree.hlDrop){
18591             dropNode.ui.focus();
18592             dropNode.ui.highlight();
18593         }
18594         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18595     },
18596     
18597     getTree : function(){
18598         return this.tree;
18599     },
18600     
18601     removeDropIndicators : function(n){
18602         if(n && n.ddel){
18603             var el = n.ddel;
18604             Roo.fly(el).removeClass([
18605                     "x-tree-drag-insert-above",
18606                     "x-tree-drag-insert-below",
18607                     "x-tree-drag-append"]);
18608             this.lastInsertClass = "_noclass";
18609         }
18610     },
18611     
18612     beforeDragDrop : function(target, e, id){
18613         this.cancelExpand();
18614         return true;
18615     },
18616     
18617     afterRepair : function(data){
18618         if(data && Roo.enableFx){
18619             data.node.ui.highlight();
18620         }
18621         this.hideProxy();
18622     } 
18623     
18624 });
18625
18626 }
18627 /*
18628  * Based on:
18629  * Ext JS Library 1.1.1
18630  * Copyright(c) 2006-2007, Ext JS, LLC.
18631  *
18632  * Originally Released Under LGPL - original licence link has changed is not relivant.
18633  *
18634  * Fork - LGPL
18635  * <script type="text/javascript">
18636  */
18637  
18638
18639 if(Roo.dd.DragZone){
18640 Roo.tree.TreeDragZone = function(tree, config){
18641     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18642     this.tree = tree;
18643 };
18644
18645 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18646     ddGroup : "TreeDD",
18647    
18648     onBeforeDrag : function(data, e){
18649         var n = data.node;
18650         return n && n.draggable && !n.disabled;
18651     },
18652      
18653     
18654     onInitDrag : function(e){
18655         var data = this.dragData;
18656         this.tree.getSelectionModel().select(data.node);
18657         this.proxy.update("");
18658         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18659         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18660     },
18661     
18662     getRepairXY : function(e, data){
18663         return data.node.ui.getDDRepairXY();
18664     },
18665     
18666     onEndDrag : function(data, e){
18667         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18668         
18669         
18670     },
18671     
18672     onValidDrop : function(dd, e, id){
18673         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18674         this.hideProxy();
18675     },
18676     
18677     beforeInvalidDrop : function(e, id){
18678         // this scrolls the original position back into view
18679         var sm = this.tree.getSelectionModel();
18680         sm.clearSelections();
18681         sm.select(this.dragData.node);
18682     }
18683 });
18684 }/*
18685  * Based on:
18686  * Ext JS Library 1.1.1
18687  * Copyright(c) 2006-2007, Ext JS, LLC.
18688  *
18689  * Originally Released Under LGPL - original licence link has changed is not relivant.
18690  *
18691  * Fork - LGPL
18692  * <script type="text/javascript">
18693  */
18694 /**
18695  * @class Roo.tree.TreeEditor
18696  * @extends Roo.Editor
18697  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
18698  * as the editor field.
18699  * @constructor
18700  * @param {Object} config (used to be the tree panel.)
18701  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
18702  * 
18703  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
18704  * @cfg {Roo.form.TextField|Object} field The field configuration
18705  *
18706  * 
18707  */
18708 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
18709     var tree = config;
18710     var field;
18711     if (oldconfig) { // old style..
18712         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
18713     } else {
18714         // new style..
18715         tree = config.tree;
18716         config.field = config.field  || {};
18717         config.field.xtype = 'TextField';
18718         field = Roo.factory(config.field, Roo.form);
18719     }
18720     config = config || {};
18721     
18722     
18723     this.addEvents({
18724         /**
18725          * @event beforenodeedit
18726          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
18727          * false from the handler of this event.
18728          * @param {Editor} this
18729          * @param {Roo.tree.Node} node 
18730          */
18731         "beforenodeedit" : true
18732     });
18733     
18734     //Roo.log(config);
18735     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
18736
18737     this.tree = tree;
18738
18739     tree.on('beforeclick', this.beforeNodeClick, this);
18740     tree.getTreeEl().on('mousedown', this.hide, this);
18741     this.on('complete', this.updateNode, this);
18742     this.on('beforestartedit', this.fitToTree, this);
18743     this.on('startedit', this.bindScroll, this, {delay:10});
18744     this.on('specialkey', this.onSpecialKey, this);
18745 };
18746
18747 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
18748     /**
18749      * @cfg {String} alignment
18750      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
18751      */
18752     alignment: "l-l",
18753     // inherit
18754     autoSize: false,
18755     /**
18756      * @cfg {Boolean} hideEl
18757      * True to hide the bound element while the editor is displayed (defaults to false)
18758      */
18759     hideEl : false,
18760     /**
18761      * @cfg {String} cls
18762      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
18763      */
18764     cls: "x-small-editor x-tree-editor",
18765     /**
18766      * @cfg {Boolean} shim
18767      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
18768      */
18769     shim:false,
18770     // inherit
18771     shadow:"frame",
18772     /**
18773      * @cfg {Number} maxWidth
18774      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
18775      * the containing tree element's size, it will be automatically limited for you to the container width, taking
18776      * scroll and client offsets into account prior to each edit.
18777      */
18778     maxWidth: 250,
18779
18780     editDelay : 350,
18781
18782     // private
18783     fitToTree : function(ed, el){
18784         var td = this.tree.getTreeEl().dom, nd = el.dom;
18785         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
18786             td.scrollLeft = nd.offsetLeft;
18787         }
18788         var w = Math.min(
18789                 this.maxWidth,
18790                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
18791         this.setSize(w, '');
18792         
18793         return this.fireEvent('beforenodeedit', this, this.editNode);
18794         
18795     },
18796
18797     // private
18798     triggerEdit : function(node){
18799         this.completeEdit();
18800         this.editNode = node;
18801         this.startEdit(node.ui.textNode, node.text);
18802     },
18803
18804     // private
18805     bindScroll : function(){
18806         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
18807     },
18808
18809     // private
18810     beforeNodeClick : function(node, e){
18811         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
18812         this.lastClick = new Date();
18813         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
18814             e.stopEvent();
18815             this.triggerEdit(node);
18816             return false;
18817         }
18818         return true;
18819     },
18820
18821     // private
18822     updateNode : function(ed, value){
18823         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
18824         this.editNode.setText(value);
18825     },
18826
18827     // private
18828     onHide : function(){
18829         Roo.tree.TreeEditor.superclass.onHide.call(this);
18830         if(this.editNode){
18831             this.editNode.ui.focus();
18832         }
18833     },
18834
18835     // private
18836     onSpecialKey : function(field, e){
18837         var k = e.getKey();
18838         if(k == e.ESC){
18839             e.stopEvent();
18840             this.cancelEdit();
18841         }else if(k == e.ENTER && !e.hasModifier()){
18842             e.stopEvent();
18843             this.completeEdit();
18844         }
18845     }
18846 });//<Script type="text/javascript">
18847 /*
18848  * Based on:
18849  * Ext JS Library 1.1.1
18850  * Copyright(c) 2006-2007, Ext JS, LLC.
18851  *
18852  * Originally Released Under LGPL - original licence link has changed is not relivant.
18853  *
18854  * Fork - LGPL
18855  * <script type="text/javascript">
18856  */
18857  
18858 /**
18859  * Not documented??? - probably should be...
18860  */
18861
18862 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
18863     //focus: Roo.emptyFn, // prevent odd scrolling behavior
18864     
18865     renderElements : function(n, a, targetNode, bulkRender){
18866         //consel.log("renderElements?");
18867         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18868
18869         var t = n.getOwnerTree();
18870         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
18871         
18872         var cols = t.columns;
18873         var bw = t.borderWidth;
18874         var c = cols[0];
18875         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18876          var cb = typeof a.checked == "boolean";
18877         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18878         var colcls = 'x-t-' + tid + '-c0';
18879         var buf = [
18880             '<li class="x-tree-node">',
18881             
18882                 
18883                 '<div class="x-tree-node-el ', a.cls,'">',
18884                     // extran...
18885                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
18886                 
18887                 
18888                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
18889                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
18890                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
18891                            (a.icon ? ' x-tree-node-inline-icon' : ''),
18892                            (a.iconCls ? ' '+a.iconCls : ''),
18893                            '" unselectable="on" />',
18894                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
18895                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
18896                              
18897                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18898                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
18899                             '<span unselectable="on" qtip="' + tx + '">',
18900                              tx,
18901                              '</span></a>' ,
18902                     '</div>',
18903                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18904                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
18905                  ];
18906         for(var i = 1, len = cols.length; i < len; i++){
18907             c = cols[i];
18908             colcls = 'x-t-' + tid + '-c' +i;
18909             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18910             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
18911                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
18912                       "</div>");
18913          }
18914          
18915          buf.push(
18916             '</a>',
18917             '<div class="x-clear"></div></div>',
18918             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18919             "</li>");
18920         
18921         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18922             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18923                                 n.nextSibling.ui.getEl(), buf.join(""));
18924         }else{
18925             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18926         }
18927         var el = this.wrap.firstChild;
18928         this.elRow = el;
18929         this.elNode = el.firstChild;
18930         this.ranchor = el.childNodes[1];
18931         this.ctNode = this.wrap.childNodes[1];
18932         var cs = el.firstChild.childNodes;
18933         this.indentNode = cs[0];
18934         this.ecNode = cs[1];
18935         this.iconNode = cs[2];
18936         var index = 3;
18937         if(cb){
18938             this.checkbox = cs[3];
18939             index++;
18940         }
18941         this.anchor = cs[index];
18942         
18943         this.textNode = cs[index].firstChild;
18944         
18945         //el.on("click", this.onClick, this);
18946         //el.on("dblclick", this.onDblClick, this);
18947         
18948         
18949        // console.log(this);
18950     },
18951     initEvents : function(){
18952         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
18953         
18954             
18955         var a = this.ranchor;
18956
18957         var el = Roo.get(a);
18958
18959         if(Roo.isOpera){ // opera render bug ignores the CSS
18960             el.setStyle("text-decoration", "none");
18961         }
18962
18963         el.on("click", this.onClick, this);
18964         el.on("dblclick", this.onDblClick, this);
18965         el.on("contextmenu", this.onContextMenu, this);
18966         
18967     },
18968     
18969     /*onSelectedChange : function(state){
18970         if(state){
18971             this.focus();
18972             this.addClass("x-tree-selected");
18973         }else{
18974             //this.blur();
18975             this.removeClass("x-tree-selected");
18976         }
18977     },*/
18978     addClass : function(cls){
18979         if(this.elRow){
18980             Roo.fly(this.elRow).addClass(cls);
18981         }
18982         
18983     },
18984     
18985     
18986     removeClass : function(cls){
18987         if(this.elRow){
18988             Roo.fly(this.elRow).removeClass(cls);
18989         }
18990     }
18991
18992     
18993     
18994 });//<Script type="text/javascript">
18995
18996 /*
18997  * Based on:
18998  * Ext JS Library 1.1.1
18999  * Copyright(c) 2006-2007, Ext JS, LLC.
19000  *
19001  * Originally Released Under LGPL - original licence link has changed is not relivant.
19002  *
19003  * Fork - LGPL
19004  * <script type="text/javascript">
19005  */
19006  
19007
19008 /**
19009  * @class Roo.tree.ColumnTree
19010  * @extends Roo.data.TreePanel
19011  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19012  * @cfg {int} borderWidth  compined right/left border allowance
19013  * @constructor
19014  * @param {String/HTMLElement/Element} el The container element
19015  * @param {Object} config
19016  */
19017 Roo.tree.ColumnTree =  function(el, config)
19018 {
19019    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19020    this.addEvents({
19021         /**
19022         * @event resize
19023         * Fire this event on a container when it resizes
19024         * @param {int} w Width
19025         * @param {int} h Height
19026         */
19027        "resize" : true
19028     });
19029     this.on('resize', this.onResize, this);
19030 };
19031
19032 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19033     //lines:false,
19034     
19035     
19036     borderWidth: Roo.isBorderBox ? 0 : 2, 
19037     headEls : false,
19038     
19039     render : function(){
19040         // add the header.....
19041        
19042         Roo.tree.ColumnTree.superclass.render.apply(this);
19043         
19044         this.el.addClass('x-column-tree');
19045         
19046         this.headers = this.el.createChild(
19047             {cls:'x-tree-headers'},this.innerCt.dom);
19048    
19049         var cols = this.columns, c;
19050         var totalWidth = 0;
19051         this.headEls = [];
19052         var  len = cols.length;
19053         for(var i = 0; i < len; i++){
19054              c = cols[i];
19055              totalWidth += c.width;
19056             this.headEls.push(this.headers.createChild({
19057                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19058                  cn: {
19059                      cls:'x-tree-hd-text',
19060                      html: c.header
19061                  },
19062                  style:'width:'+(c.width-this.borderWidth)+'px;'
19063              }));
19064         }
19065         this.headers.createChild({cls:'x-clear'});
19066         // prevent floats from wrapping when clipped
19067         this.headers.setWidth(totalWidth);
19068         //this.innerCt.setWidth(totalWidth);
19069         this.innerCt.setStyle({ overflow: 'auto' });
19070         this.onResize(this.width, this.height);
19071              
19072         
19073     },
19074     onResize : function(w,h)
19075     {
19076         this.height = h;
19077         this.width = w;
19078         // resize cols..
19079         this.innerCt.setWidth(this.width);
19080         this.innerCt.setHeight(this.height-20);
19081         
19082         // headers...
19083         var cols = this.columns, c;
19084         var totalWidth = 0;
19085         var expEl = false;
19086         var len = cols.length;
19087         for(var i = 0; i < len; i++){
19088             c = cols[i];
19089             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19090                 // it's the expander..
19091                 expEl  = this.headEls[i];
19092                 continue;
19093             }
19094             totalWidth += c.width;
19095             
19096         }
19097         if (expEl) {
19098             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19099         }
19100         this.headers.setWidth(w-20);
19101
19102         
19103         
19104         
19105     }
19106 });
19107 /*
19108  * Based on:
19109  * Ext JS Library 1.1.1
19110  * Copyright(c) 2006-2007, Ext JS, LLC.
19111  *
19112  * Originally Released Under LGPL - original licence link has changed is not relivant.
19113  *
19114  * Fork - LGPL
19115  * <script type="text/javascript">
19116  */
19117  
19118 /**
19119  * @class Roo.menu.Menu
19120  * @extends Roo.util.Observable
19121  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19122  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19123  * @constructor
19124  * Creates a new Menu
19125  * @param {Object} config Configuration options
19126  */
19127 Roo.menu.Menu = function(config){
19128     Roo.apply(this, config);
19129     this.id = this.id || Roo.id();
19130     this.addEvents({
19131         /**
19132          * @event beforeshow
19133          * Fires before this menu is displayed
19134          * @param {Roo.menu.Menu} this
19135          */
19136         beforeshow : true,
19137         /**
19138          * @event beforehide
19139          * Fires before this menu is hidden
19140          * @param {Roo.menu.Menu} this
19141          */
19142         beforehide : true,
19143         /**
19144          * @event show
19145          * Fires after this menu is displayed
19146          * @param {Roo.menu.Menu} this
19147          */
19148         show : true,
19149         /**
19150          * @event hide
19151          * Fires after this menu is hidden
19152          * @param {Roo.menu.Menu} this
19153          */
19154         hide : true,
19155         /**
19156          * @event click
19157          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19158          * @param {Roo.menu.Menu} this
19159          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19160          * @param {Roo.EventObject} e
19161          */
19162         click : true,
19163         /**
19164          * @event mouseover
19165          * Fires when the mouse is hovering over this menu
19166          * @param {Roo.menu.Menu} this
19167          * @param {Roo.EventObject} e
19168          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19169          */
19170         mouseover : true,
19171         /**
19172          * @event mouseout
19173          * Fires when the mouse exits this menu
19174          * @param {Roo.menu.Menu} this
19175          * @param {Roo.EventObject} e
19176          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19177          */
19178         mouseout : true,
19179         /**
19180          * @event itemclick
19181          * Fires when a menu item contained in this menu is clicked
19182          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19183          * @param {Roo.EventObject} e
19184          */
19185         itemclick: true
19186     });
19187     if (this.registerMenu) {
19188         Roo.menu.MenuMgr.register(this);
19189     }
19190     
19191     var mis = this.items;
19192     this.items = new Roo.util.MixedCollection();
19193     if(mis){
19194         this.add.apply(this, mis);
19195     }
19196 };
19197
19198 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19199     /**
19200      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19201      */
19202     minWidth : 120,
19203     /**
19204      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19205      * for bottom-right shadow (defaults to "sides")
19206      */
19207     shadow : "sides",
19208     /**
19209      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19210      * this menu (defaults to "tl-tr?")
19211      */
19212     subMenuAlign : "tl-tr?",
19213     /**
19214      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19215      * relative to its element of origin (defaults to "tl-bl?")
19216      */
19217     defaultAlign : "tl-bl?",
19218     /**
19219      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19220      */
19221     allowOtherMenus : false,
19222     /**
19223      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19224      */
19225     registerMenu : true,
19226
19227     hidden:true,
19228
19229     // private
19230     render : function(){
19231         if(this.el){
19232             return;
19233         }
19234         var el = this.el = new Roo.Layer({
19235             cls: "x-menu",
19236             shadow:this.shadow,
19237             constrain: false,
19238             parentEl: this.parentEl || document.body,
19239             zindex:15000
19240         });
19241
19242         this.keyNav = new Roo.menu.MenuNav(this);
19243
19244         if(this.plain){
19245             el.addClass("x-menu-plain");
19246         }
19247         if(this.cls){
19248             el.addClass(this.cls);
19249         }
19250         // generic focus element
19251         this.focusEl = el.createChild({
19252             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19253         });
19254         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19255         //disabling touch- as it's causing issues ..
19256         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
19257         ul.on('click'   , this.onClick, this);
19258         
19259         
19260         ul.on("mouseover", this.onMouseOver, this);
19261         ul.on("mouseout", this.onMouseOut, this);
19262         this.items.each(function(item){
19263             if (item.hidden) {
19264                 return;
19265             }
19266             
19267             var li = document.createElement("li");
19268             li.className = "x-menu-list-item";
19269             ul.dom.appendChild(li);
19270             item.render(li, this);
19271         }, this);
19272         this.ul = ul;
19273         this.autoWidth();
19274     },
19275
19276     // private
19277     autoWidth : function(){
19278         var el = this.el, ul = this.ul;
19279         if(!el){
19280             return;
19281         }
19282         var w = this.width;
19283         if(w){
19284             el.setWidth(w);
19285         }else if(Roo.isIE){
19286             el.setWidth(this.minWidth);
19287             var t = el.dom.offsetWidth; // force recalc
19288             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19289         }
19290     },
19291
19292     // private
19293     delayAutoWidth : function(){
19294         if(this.rendered){
19295             if(!this.awTask){
19296                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19297             }
19298             this.awTask.delay(20);
19299         }
19300     },
19301
19302     // private
19303     findTargetItem : function(e){
19304         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19305         if(t && t.menuItemId){
19306             return this.items.get(t.menuItemId);
19307         }
19308     },
19309
19310     // private
19311     onClick : function(e){
19312         Roo.log("menu.onClick");
19313         var t = this.findTargetItem(e);
19314         if(!t){
19315             return;
19316         }
19317         Roo.log(e);
19318         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
19319             if(t == this.activeItem && t.shouldDeactivate(e)){
19320                 this.activeItem.deactivate();
19321                 delete this.activeItem;
19322                 return;
19323             }
19324             if(t.canActivate){
19325                 this.setActiveItem(t, true);
19326             }
19327             return;
19328             
19329             
19330         }
19331         
19332         t.onClick(e);
19333         this.fireEvent("click", this, t, e);
19334     },
19335
19336     // private
19337     setActiveItem : function(item, autoExpand){
19338         if(item != this.activeItem){
19339             if(this.activeItem){
19340                 this.activeItem.deactivate();
19341             }
19342             this.activeItem = item;
19343             item.activate(autoExpand);
19344         }else if(autoExpand){
19345             item.expandMenu();
19346         }
19347     },
19348
19349     // private
19350     tryActivate : function(start, step){
19351         var items = this.items;
19352         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19353             var item = items.get(i);
19354             if(!item.disabled && item.canActivate){
19355                 this.setActiveItem(item, false);
19356                 return item;
19357             }
19358         }
19359         return false;
19360     },
19361
19362     // private
19363     onMouseOver : function(e){
19364         var t;
19365         if(t = this.findTargetItem(e)){
19366             if(t.canActivate && !t.disabled){
19367                 this.setActiveItem(t, true);
19368             }
19369         }
19370         this.fireEvent("mouseover", this, e, t);
19371     },
19372
19373     // private
19374     onMouseOut : function(e){
19375         var t;
19376         if(t = this.findTargetItem(e)){
19377             if(t == this.activeItem && t.shouldDeactivate(e)){
19378                 this.activeItem.deactivate();
19379                 delete this.activeItem;
19380             }
19381         }
19382         this.fireEvent("mouseout", this, e, t);
19383     },
19384
19385     /**
19386      * Read-only.  Returns true if the menu is currently displayed, else false.
19387      * @type Boolean
19388      */
19389     isVisible : function(){
19390         return this.el && !this.hidden;
19391     },
19392
19393     /**
19394      * Displays this menu relative to another element
19395      * @param {String/HTMLElement/Roo.Element} element The element to align to
19396      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19397      * the element (defaults to this.defaultAlign)
19398      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19399      */
19400     show : function(el, pos, parentMenu){
19401         this.parentMenu = parentMenu;
19402         if(!this.el){
19403             this.render();
19404         }
19405         this.fireEvent("beforeshow", this);
19406         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19407     },
19408
19409     /**
19410      * Displays this menu at a specific xy position
19411      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19412      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19413      */
19414     showAt : function(xy, parentMenu, /* private: */_e){
19415         this.parentMenu = parentMenu;
19416         if(!this.el){
19417             this.render();
19418         }
19419         if(_e !== false){
19420             this.fireEvent("beforeshow", this);
19421             xy = this.el.adjustForConstraints(xy);
19422         }
19423         this.el.setXY(xy);
19424         this.el.show();
19425         this.hidden = false;
19426         this.focus();
19427         this.fireEvent("show", this);
19428     },
19429
19430     focus : function(){
19431         if(!this.hidden){
19432             this.doFocus.defer(50, this);
19433         }
19434     },
19435
19436     doFocus : function(){
19437         if(!this.hidden){
19438             this.focusEl.focus();
19439         }
19440     },
19441
19442     /**
19443      * Hides this menu and optionally all parent menus
19444      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19445      */
19446     hide : function(deep){
19447         if(this.el && this.isVisible()){
19448             this.fireEvent("beforehide", this);
19449             if(this.activeItem){
19450                 this.activeItem.deactivate();
19451                 this.activeItem = null;
19452             }
19453             this.el.hide();
19454             this.hidden = true;
19455             this.fireEvent("hide", this);
19456         }
19457         if(deep === true && this.parentMenu){
19458             this.parentMenu.hide(true);
19459         }
19460     },
19461
19462     /**
19463      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19464      * Any of the following are valid:
19465      * <ul>
19466      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19467      * <li>An HTMLElement object which will be converted to a menu item</li>
19468      * <li>A menu item config object that will be created as a new menu item</li>
19469      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19470      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19471      * </ul>
19472      * Usage:
19473      * <pre><code>
19474 // Create the menu
19475 var menu = new Roo.menu.Menu();
19476
19477 // Create a menu item to add by reference
19478 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19479
19480 // Add a bunch of items at once using different methods.
19481 // Only the last item added will be returned.
19482 var item = menu.add(
19483     menuItem,                // add existing item by ref
19484     'Dynamic Item',          // new TextItem
19485     '-',                     // new separator
19486     { text: 'Config Item' }  // new item by config
19487 );
19488 </code></pre>
19489      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19490      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19491      */
19492     add : function(){
19493         var a = arguments, l = a.length, item;
19494         for(var i = 0; i < l; i++){
19495             var el = a[i];
19496             if ((typeof(el) == "object") && el.xtype && el.xns) {
19497                 el = Roo.factory(el, Roo.menu);
19498             }
19499             
19500             if(el.render){ // some kind of Item
19501                 item = this.addItem(el);
19502             }else if(typeof el == "string"){ // string
19503                 if(el == "separator" || el == "-"){
19504                     item = this.addSeparator();
19505                 }else{
19506                     item = this.addText(el);
19507                 }
19508             }else if(el.tagName || el.el){ // element
19509                 item = this.addElement(el);
19510             }else if(typeof el == "object"){ // must be menu item config?
19511                 item = this.addMenuItem(el);
19512             }
19513         }
19514         return item;
19515     },
19516
19517     /**
19518      * Returns this menu's underlying {@link Roo.Element} object
19519      * @return {Roo.Element} The element
19520      */
19521     getEl : function(){
19522         if(!this.el){
19523             this.render();
19524         }
19525         return this.el;
19526     },
19527
19528     /**
19529      * Adds a separator bar to the menu
19530      * @return {Roo.menu.Item} The menu item that was added
19531      */
19532     addSeparator : function(){
19533         return this.addItem(new Roo.menu.Separator());
19534     },
19535
19536     /**
19537      * Adds an {@link Roo.Element} object to the menu
19538      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19539      * @return {Roo.menu.Item} The menu item that was added
19540      */
19541     addElement : function(el){
19542         return this.addItem(new Roo.menu.BaseItem(el));
19543     },
19544
19545     /**
19546      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19547      * @param {Roo.menu.Item} item The menu item to add
19548      * @return {Roo.menu.Item} The menu item that was added
19549      */
19550     addItem : function(item){
19551         this.items.add(item);
19552         if(this.ul){
19553             var li = document.createElement("li");
19554             li.className = "x-menu-list-item";
19555             this.ul.dom.appendChild(li);
19556             item.render(li, this);
19557             this.delayAutoWidth();
19558         }
19559         return item;
19560     },
19561
19562     /**
19563      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19564      * @param {Object} config A MenuItem config object
19565      * @return {Roo.menu.Item} The menu item that was added
19566      */
19567     addMenuItem : function(config){
19568         if(!(config instanceof Roo.menu.Item)){
19569             if(typeof config.checked == "boolean"){ // must be check menu item config?
19570                 config = new Roo.menu.CheckItem(config);
19571             }else{
19572                 config = new Roo.menu.Item(config);
19573             }
19574         }
19575         return this.addItem(config);
19576     },
19577
19578     /**
19579      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19580      * @param {String} text The text to display in the menu item
19581      * @return {Roo.menu.Item} The menu item that was added
19582      */
19583     addText : function(text){
19584         return this.addItem(new Roo.menu.TextItem({ text : text }));
19585     },
19586
19587     /**
19588      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19589      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19590      * @param {Roo.menu.Item} item The menu item to add
19591      * @return {Roo.menu.Item} The menu item that was added
19592      */
19593     insert : function(index, item){
19594         this.items.insert(index, item);
19595         if(this.ul){
19596             var li = document.createElement("li");
19597             li.className = "x-menu-list-item";
19598             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19599             item.render(li, this);
19600             this.delayAutoWidth();
19601         }
19602         return item;
19603     },
19604
19605     /**
19606      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19607      * @param {Roo.menu.Item} item The menu item to remove
19608      */
19609     remove : function(item){
19610         this.items.removeKey(item.id);
19611         item.destroy();
19612     },
19613
19614     /**
19615      * Removes and destroys all items in the menu
19616      */
19617     removeAll : function(){
19618         var f;
19619         while(f = this.items.first()){
19620             this.remove(f);
19621         }
19622     }
19623 });
19624
19625 // MenuNav is a private utility class used internally by the Menu
19626 Roo.menu.MenuNav = function(menu){
19627     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19628     this.scope = this.menu = menu;
19629 };
19630
19631 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19632     doRelay : function(e, h){
19633         var k = e.getKey();
19634         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19635             this.menu.tryActivate(0, 1);
19636             return false;
19637         }
19638         return h.call(this.scope || this, e, this.menu);
19639     },
19640
19641     up : function(e, m){
19642         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19643             m.tryActivate(m.items.length-1, -1);
19644         }
19645     },
19646
19647     down : function(e, m){
19648         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19649             m.tryActivate(0, 1);
19650         }
19651     },
19652
19653     right : function(e, m){
19654         if(m.activeItem){
19655             m.activeItem.expandMenu(true);
19656         }
19657     },
19658
19659     left : function(e, m){
19660         m.hide();
19661         if(m.parentMenu && m.parentMenu.activeItem){
19662             m.parentMenu.activeItem.activate();
19663         }
19664     },
19665
19666     enter : function(e, m){
19667         if(m.activeItem){
19668             e.stopPropagation();
19669             m.activeItem.onClick(e);
19670             m.fireEvent("click", this, m.activeItem);
19671             return true;
19672         }
19673     }
19674 });/*
19675  * Based on:
19676  * Ext JS Library 1.1.1
19677  * Copyright(c) 2006-2007, Ext JS, LLC.
19678  *
19679  * Originally Released Under LGPL - original licence link has changed is not relivant.
19680  *
19681  * Fork - LGPL
19682  * <script type="text/javascript">
19683  */
19684  
19685 /**
19686  * @class Roo.menu.MenuMgr
19687  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19688  * @singleton
19689  */
19690 Roo.menu.MenuMgr = function(){
19691    var menus, active, groups = {}, attached = false, lastShow = new Date();
19692
19693    // private - called when first menu is created
19694    function init(){
19695        menus = {};
19696        active = new Roo.util.MixedCollection();
19697        Roo.get(document).addKeyListener(27, function(){
19698            if(active.length > 0){
19699                hideAll();
19700            }
19701        });
19702    }
19703
19704    // private
19705    function hideAll(){
19706        if(active && active.length > 0){
19707            var c = active.clone();
19708            c.each(function(m){
19709                m.hide();
19710            });
19711        }
19712    }
19713
19714    // private
19715    function onHide(m){
19716        active.remove(m);
19717        if(active.length < 1){
19718            Roo.get(document).un("mousedown", onMouseDown);
19719            attached = false;
19720        }
19721    }
19722
19723    // private
19724    function onShow(m){
19725        var last = active.last();
19726        lastShow = new Date();
19727        active.add(m);
19728        if(!attached){
19729            Roo.get(document).on("mousedown", onMouseDown);
19730            attached = true;
19731        }
19732        if(m.parentMenu){
19733           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19734           m.parentMenu.activeChild = m;
19735        }else if(last && last.isVisible()){
19736           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19737        }
19738    }
19739
19740    // private
19741    function onBeforeHide(m){
19742        if(m.activeChild){
19743            m.activeChild.hide();
19744        }
19745        if(m.autoHideTimer){
19746            clearTimeout(m.autoHideTimer);
19747            delete m.autoHideTimer;
19748        }
19749    }
19750
19751    // private
19752    function onBeforeShow(m){
19753        var pm = m.parentMenu;
19754        if(!pm && !m.allowOtherMenus){
19755            hideAll();
19756        }else if(pm && pm.activeChild && active != m){
19757            pm.activeChild.hide();
19758        }
19759    }
19760
19761    // private
19762    function onMouseDown(e){
19763        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
19764            hideAll();
19765        }
19766    }
19767
19768    // private
19769    function onBeforeCheck(mi, state){
19770        if(state){
19771            var g = groups[mi.group];
19772            for(var i = 0, l = g.length; i < l; i++){
19773                if(g[i] != mi){
19774                    g[i].setChecked(false);
19775                }
19776            }
19777        }
19778    }
19779
19780    return {
19781
19782        /**
19783         * Hides all menus that are currently visible
19784         */
19785        hideAll : function(){
19786             hideAll();  
19787        },
19788
19789        // private
19790        register : function(menu){
19791            if(!menus){
19792                init();
19793            }
19794            menus[menu.id] = menu;
19795            menu.on("beforehide", onBeforeHide);
19796            menu.on("hide", onHide);
19797            menu.on("beforeshow", onBeforeShow);
19798            menu.on("show", onShow);
19799            var g = menu.group;
19800            if(g && menu.events["checkchange"]){
19801                if(!groups[g]){
19802                    groups[g] = [];
19803                }
19804                groups[g].push(menu);
19805                menu.on("checkchange", onCheck);
19806            }
19807        },
19808
19809         /**
19810          * Returns a {@link Roo.menu.Menu} object
19811          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
19812          * be used to generate and return a new Menu instance.
19813          */
19814        get : function(menu){
19815            if(typeof menu == "string"){ // menu id
19816                return menus[menu];
19817            }else if(menu.events){  // menu instance
19818                return menu;
19819            }else if(typeof menu.length == 'number'){ // array of menu items?
19820                return new Roo.menu.Menu({items:menu});
19821            }else{ // otherwise, must be a config
19822                return new Roo.menu.Menu(menu);
19823            }
19824        },
19825
19826        // private
19827        unregister : function(menu){
19828            delete menus[menu.id];
19829            menu.un("beforehide", onBeforeHide);
19830            menu.un("hide", onHide);
19831            menu.un("beforeshow", onBeforeShow);
19832            menu.un("show", onShow);
19833            var g = menu.group;
19834            if(g && menu.events["checkchange"]){
19835                groups[g].remove(menu);
19836                menu.un("checkchange", onCheck);
19837            }
19838        },
19839
19840        // private
19841        registerCheckable : function(menuItem){
19842            var g = menuItem.group;
19843            if(g){
19844                if(!groups[g]){
19845                    groups[g] = [];
19846                }
19847                groups[g].push(menuItem);
19848                menuItem.on("beforecheckchange", onBeforeCheck);
19849            }
19850        },
19851
19852        // private
19853        unregisterCheckable : function(menuItem){
19854            var g = menuItem.group;
19855            if(g){
19856                groups[g].remove(menuItem);
19857                menuItem.un("beforecheckchange", onBeforeCheck);
19858            }
19859        }
19860    };
19861 }();/*
19862  * Based on:
19863  * Ext JS Library 1.1.1
19864  * Copyright(c) 2006-2007, Ext JS, LLC.
19865  *
19866  * Originally Released Under LGPL - original licence link has changed is not relivant.
19867  *
19868  * Fork - LGPL
19869  * <script type="text/javascript">
19870  */
19871  
19872
19873 /**
19874  * @class Roo.menu.BaseItem
19875  * @extends Roo.Component
19876  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
19877  * management and base configuration options shared by all menu components.
19878  * @constructor
19879  * Creates a new BaseItem
19880  * @param {Object} config Configuration options
19881  */
19882 Roo.menu.BaseItem = function(config){
19883     Roo.menu.BaseItem.superclass.constructor.call(this, config);
19884
19885     this.addEvents({
19886         /**
19887          * @event click
19888          * Fires when this item is clicked
19889          * @param {Roo.menu.BaseItem} this
19890          * @param {Roo.EventObject} e
19891          */
19892         click: true,
19893         /**
19894          * @event activate
19895          * Fires when this item is activated
19896          * @param {Roo.menu.BaseItem} this
19897          */
19898         activate : true,
19899         /**
19900          * @event deactivate
19901          * Fires when this item is deactivated
19902          * @param {Roo.menu.BaseItem} this
19903          */
19904         deactivate : true
19905     });
19906
19907     if(this.handler){
19908         this.on("click", this.handler, this.scope, true);
19909     }
19910 };
19911
19912 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
19913     /**
19914      * @cfg {Function} handler
19915      * A function that will handle the click event of this menu item (defaults to undefined)
19916      */
19917     /**
19918      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
19919      */
19920     canActivate : false,
19921     
19922      /**
19923      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
19924      */
19925     hidden: false,
19926     
19927     /**
19928      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
19929      */
19930     activeClass : "x-menu-item-active",
19931     /**
19932      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
19933      */
19934     hideOnClick : true,
19935     /**
19936      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
19937      */
19938     hideDelay : 100,
19939
19940     // private
19941     ctype: "Roo.menu.BaseItem",
19942
19943     // private
19944     actionMode : "container",
19945
19946     // private
19947     render : function(container, parentMenu){
19948         this.parentMenu = parentMenu;
19949         Roo.menu.BaseItem.superclass.render.call(this, container);
19950         this.container.menuItemId = this.id;
19951     },
19952
19953     // private
19954     onRender : function(container, position){
19955         this.el = Roo.get(this.el);
19956         container.dom.appendChild(this.el.dom);
19957     },
19958
19959     // private
19960     onClick : function(e){
19961         if(!this.disabled && this.fireEvent("click", this, e) !== false
19962                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
19963             this.handleClick(e);
19964         }else{
19965             e.stopEvent();
19966         }
19967     },
19968
19969     // private
19970     activate : function(){
19971         if(this.disabled){
19972             return false;
19973         }
19974         var li = this.container;
19975         li.addClass(this.activeClass);
19976         this.region = li.getRegion().adjust(2, 2, -2, -2);
19977         this.fireEvent("activate", this);
19978         return true;
19979     },
19980
19981     // private
19982     deactivate : function(){
19983         this.container.removeClass(this.activeClass);
19984         this.fireEvent("deactivate", this);
19985     },
19986
19987     // private
19988     shouldDeactivate : function(e){
19989         return !this.region || !this.region.contains(e.getPoint());
19990     },
19991
19992     // private
19993     handleClick : function(e){
19994         if(this.hideOnClick){
19995             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
19996         }
19997     },
19998
19999     // private
20000     expandMenu : function(autoActivate){
20001         // do nothing
20002     },
20003
20004     // private
20005     hideMenu : function(){
20006         // do nothing
20007     }
20008 });/*
20009  * Based on:
20010  * Ext JS Library 1.1.1
20011  * Copyright(c) 2006-2007, Ext JS, LLC.
20012  *
20013  * Originally Released Under LGPL - original licence link has changed is not relivant.
20014  *
20015  * Fork - LGPL
20016  * <script type="text/javascript">
20017  */
20018  
20019 /**
20020  * @class Roo.menu.Adapter
20021  * @extends Roo.menu.BaseItem
20022  * 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.
20023  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20024  * @constructor
20025  * Creates a new Adapter
20026  * @param {Object} config Configuration options
20027  */
20028 Roo.menu.Adapter = function(component, config){
20029     Roo.menu.Adapter.superclass.constructor.call(this, config);
20030     this.component = component;
20031 };
20032 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20033     // private
20034     canActivate : true,
20035
20036     // private
20037     onRender : function(container, position){
20038         this.component.render(container);
20039         this.el = this.component.getEl();
20040     },
20041
20042     // private
20043     activate : function(){
20044         if(this.disabled){
20045             return false;
20046         }
20047         this.component.focus();
20048         this.fireEvent("activate", this);
20049         return true;
20050     },
20051
20052     // private
20053     deactivate : function(){
20054         this.fireEvent("deactivate", this);
20055     },
20056
20057     // private
20058     disable : function(){
20059         this.component.disable();
20060         Roo.menu.Adapter.superclass.disable.call(this);
20061     },
20062
20063     // private
20064     enable : function(){
20065         this.component.enable();
20066         Roo.menu.Adapter.superclass.enable.call(this);
20067     }
20068 });/*
20069  * Based on:
20070  * Ext JS Library 1.1.1
20071  * Copyright(c) 2006-2007, Ext JS, LLC.
20072  *
20073  * Originally Released Under LGPL - original licence link has changed is not relivant.
20074  *
20075  * Fork - LGPL
20076  * <script type="text/javascript">
20077  */
20078
20079 /**
20080  * @class Roo.menu.TextItem
20081  * @extends Roo.menu.BaseItem
20082  * Adds a static text string to a menu, usually used as either a heading or group separator.
20083  * Note: old style constructor with text is still supported.
20084  * 
20085  * @constructor
20086  * Creates a new TextItem
20087  * @param {Object} cfg Configuration
20088  */
20089 Roo.menu.TextItem = function(cfg){
20090     if (typeof(cfg) == 'string') {
20091         this.text = cfg;
20092     } else {
20093         Roo.apply(this,cfg);
20094     }
20095     
20096     Roo.menu.TextItem.superclass.constructor.call(this);
20097 };
20098
20099 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20100     /**
20101      * @cfg {Boolean} text Text to show on item.
20102      */
20103     text : '',
20104     
20105     /**
20106      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20107      */
20108     hideOnClick : false,
20109     /**
20110      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20111      */
20112     itemCls : "x-menu-text",
20113
20114     // private
20115     onRender : function(){
20116         var s = document.createElement("span");
20117         s.className = this.itemCls;
20118         s.innerHTML = this.text;
20119         this.el = s;
20120         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20121     }
20122 });/*
20123  * Based on:
20124  * Ext JS Library 1.1.1
20125  * Copyright(c) 2006-2007, Ext JS, LLC.
20126  *
20127  * Originally Released Under LGPL - original licence link has changed is not relivant.
20128  *
20129  * Fork - LGPL
20130  * <script type="text/javascript">
20131  */
20132
20133 /**
20134  * @class Roo.menu.Separator
20135  * @extends Roo.menu.BaseItem
20136  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20137  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20138  * @constructor
20139  * @param {Object} config Configuration options
20140  */
20141 Roo.menu.Separator = function(config){
20142     Roo.menu.Separator.superclass.constructor.call(this, config);
20143 };
20144
20145 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20146     /**
20147      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20148      */
20149     itemCls : "x-menu-sep",
20150     /**
20151      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20152      */
20153     hideOnClick : false,
20154
20155     // private
20156     onRender : function(li){
20157         var s = document.createElement("span");
20158         s.className = this.itemCls;
20159         s.innerHTML = "&#160;";
20160         this.el = s;
20161         li.addClass("x-menu-sep-li");
20162         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20163     }
20164 });/*
20165  * Based on:
20166  * Ext JS Library 1.1.1
20167  * Copyright(c) 2006-2007, Ext JS, LLC.
20168  *
20169  * Originally Released Under LGPL - original licence link has changed is not relivant.
20170  *
20171  * Fork - LGPL
20172  * <script type="text/javascript">
20173  */
20174 /**
20175  * @class Roo.menu.Item
20176  * @extends Roo.menu.BaseItem
20177  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20178  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20179  * activation and click handling.
20180  * @constructor
20181  * Creates a new Item
20182  * @param {Object} config Configuration options
20183  */
20184 Roo.menu.Item = function(config){
20185     Roo.menu.Item.superclass.constructor.call(this, config);
20186     if(this.menu){
20187         this.menu = Roo.menu.MenuMgr.get(this.menu);
20188     }
20189 };
20190 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20191     
20192     /**
20193      * @cfg {String} text
20194      * The text to show on the menu item.
20195      */
20196     text: '',
20197      /**
20198      * @cfg {String} HTML to render in menu
20199      * The text to show on the menu item (HTML version).
20200      */
20201     html: '',
20202     /**
20203      * @cfg {String} icon
20204      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20205      */
20206     icon: undefined,
20207     /**
20208      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20209      */
20210     itemCls : "x-menu-item",
20211     /**
20212      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20213      */
20214     canActivate : true,
20215     /**
20216      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20217      */
20218     showDelay: 200,
20219     // doc'd in BaseItem
20220     hideDelay: 200,
20221
20222     // private
20223     ctype: "Roo.menu.Item",
20224     
20225     // private
20226     onRender : function(container, position){
20227         var el = document.createElement("a");
20228         el.hideFocus = true;
20229         el.unselectable = "on";
20230         el.href = this.href || "#";
20231         if(this.hrefTarget){
20232             el.target = this.hrefTarget;
20233         }
20234         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20235         
20236         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20237         
20238         el.innerHTML = String.format(
20239                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20240                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20241         this.el = el;
20242         Roo.menu.Item.superclass.onRender.call(this, container, position);
20243     },
20244
20245     /**
20246      * Sets the text to display in this menu item
20247      * @param {String} text The text to display
20248      * @param {Boolean} isHTML true to indicate text is pure html.
20249      */
20250     setText : function(text, isHTML){
20251         if (isHTML) {
20252             this.html = text;
20253         } else {
20254             this.text = text;
20255             this.html = '';
20256         }
20257         if(this.rendered){
20258             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20259      
20260             this.el.update(String.format(
20261                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20262                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20263             this.parentMenu.autoWidth();
20264         }
20265     },
20266
20267     // private
20268     handleClick : function(e){
20269         if(!this.href){ // if no link defined, stop the event automatically
20270             e.stopEvent();
20271         }
20272         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20273     },
20274
20275     // private
20276     activate : function(autoExpand){
20277         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20278             this.focus();
20279             if(autoExpand){
20280                 this.expandMenu();
20281             }
20282         }
20283         return true;
20284     },
20285
20286     // private
20287     shouldDeactivate : function(e){
20288         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20289             if(this.menu && this.menu.isVisible()){
20290                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20291             }
20292             return true;
20293         }
20294         return false;
20295     },
20296
20297     // private
20298     deactivate : function(){
20299         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20300         this.hideMenu();
20301     },
20302
20303     // private
20304     expandMenu : function(autoActivate){
20305         if(!this.disabled && this.menu){
20306             clearTimeout(this.hideTimer);
20307             delete this.hideTimer;
20308             if(!this.menu.isVisible() && !this.showTimer){
20309                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20310             }else if (this.menu.isVisible() && autoActivate){
20311                 this.menu.tryActivate(0, 1);
20312             }
20313         }
20314     },
20315
20316     // private
20317     deferExpand : function(autoActivate){
20318         delete this.showTimer;
20319         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20320         if(autoActivate){
20321             this.menu.tryActivate(0, 1);
20322         }
20323     },
20324
20325     // private
20326     hideMenu : function(){
20327         clearTimeout(this.showTimer);
20328         delete this.showTimer;
20329         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20330             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20331         }
20332     },
20333
20334     // private
20335     deferHide : function(){
20336         delete this.hideTimer;
20337         this.menu.hide();
20338     }
20339 });/*
20340  * Based on:
20341  * Ext JS Library 1.1.1
20342  * Copyright(c) 2006-2007, Ext JS, LLC.
20343  *
20344  * Originally Released Under LGPL - original licence link has changed is not relivant.
20345  *
20346  * Fork - LGPL
20347  * <script type="text/javascript">
20348  */
20349  
20350 /**
20351  * @class Roo.menu.CheckItem
20352  * @extends Roo.menu.Item
20353  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20354  * @constructor
20355  * Creates a new CheckItem
20356  * @param {Object} config Configuration options
20357  */
20358 Roo.menu.CheckItem = function(config){
20359     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20360     this.addEvents({
20361         /**
20362          * @event beforecheckchange
20363          * Fires before the checked value is set, providing an opportunity to cancel if needed
20364          * @param {Roo.menu.CheckItem} this
20365          * @param {Boolean} checked The new checked value that will be set
20366          */
20367         "beforecheckchange" : true,
20368         /**
20369          * @event checkchange
20370          * Fires after the checked value has been set
20371          * @param {Roo.menu.CheckItem} this
20372          * @param {Boolean} checked The checked value that was set
20373          */
20374         "checkchange" : true
20375     });
20376     if(this.checkHandler){
20377         this.on('checkchange', this.checkHandler, this.scope);
20378     }
20379 };
20380 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20381     /**
20382      * @cfg {String} group
20383      * All check items with the same group name will automatically be grouped into a single-select
20384      * radio button group (defaults to '')
20385      */
20386     /**
20387      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20388      */
20389     itemCls : "x-menu-item x-menu-check-item",
20390     /**
20391      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20392      */
20393     groupClass : "x-menu-group-item",
20394
20395     /**
20396      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20397      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20398      * initialized with checked = true will be rendered as checked.
20399      */
20400     checked: false,
20401
20402     // private
20403     ctype: "Roo.menu.CheckItem",
20404
20405     // private
20406     onRender : function(c){
20407         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20408         if(this.group){
20409             this.el.addClass(this.groupClass);
20410         }
20411         Roo.menu.MenuMgr.registerCheckable(this);
20412         if(this.checked){
20413             this.checked = false;
20414             this.setChecked(true, true);
20415         }
20416     },
20417
20418     // private
20419     destroy : function(){
20420         if(this.rendered){
20421             Roo.menu.MenuMgr.unregisterCheckable(this);
20422         }
20423         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20424     },
20425
20426     /**
20427      * Set the checked state of this item
20428      * @param {Boolean} checked The new checked value
20429      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20430      */
20431     setChecked : function(state, suppressEvent){
20432         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20433             if(this.container){
20434                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20435             }
20436             this.checked = state;
20437             if(suppressEvent !== true){
20438                 this.fireEvent("checkchange", this, state);
20439             }
20440         }
20441     },
20442
20443     // private
20444     handleClick : function(e){
20445        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20446            this.setChecked(!this.checked);
20447        }
20448        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20449     }
20450 });/*
20451  * Based on:
20452  * Ext JS Library 1.1.1
20453  * Copyright(c) 2006-2007, Ext JS, LLC.
20454  *
20455  * Originally Released Under LGPL - original licence link has changed is not relivant.
20456  *
20457  * Fork - LGPL
20458  * <script type="text/javascript">
20459  */
20460  
20461 /**
20462  * @class Roo.menu.DateItem
20463  * @extends Roo.menu.Adapter
20464  * A menu item that wraps the {@link Roo.DatPicker} component.
20465  * @constructor
20466  * Creates a new DateItem
20467  * @param {Object} config Configuration options
20468  */
20469 Roo.menu.DateItem = function(config){
20470     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20471     /** The Roo.DatePicker object @type Roo.DatePicker */
20472     this.picker = this.component;
20473     this.addEvents({select: true});
20474     
20475     this.picker.on("render", function(picker){
20476         picker.getEl().swallowEvent("click");
20477         picker.container.addClass("x-menu-date-item");
20478     });
20479
20480     this.picker.on("select", this.onSelect, this);
20481 };
20482
20483 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20484     // private
20485     onSelect : function(picker, date){
20486         this.fireEvent("select", this, date, picker);
20487         Roo.menu.DateItem.superclass.handleClick.call(this);
20488     }
20489 });/*
20490  * Based on:
20491  * Ext JS Library 1.1.1
20492  * Copyright(c) 2006-2007, Ext JS, LLC.
20493  *
20494  * Originally Released Under LGPL - original licence link has changed is not relivant.
20495  *
20496  * Fork - LGPL
20497  * <script type="text/javascript">
20498  */
20499  
20500 /**
20501  * @class Roo.menu.ColorItem
20502  * @extends Roo.menu.Adapter
20503  * A menu item that wraps the {@link Roo.ColorPalette} component.
20504  * @constructor
20505  * Creates a new ColorItem
20506  * @param {Object} config Configuration options
20507  */
20508 Roo.menu.ColorItem = function(config){
20509     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20510     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20511     this.palette = this.component;
20512     this.relayEvents(this.palette, ["select"]);
20513     if(this.selectHandler){
20514         this.on('select', this.selectHandler, this.scope);
20515     }
20516 };
20517 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20518  * Based on:
20519  * Ext JS Library 1.1.1
20520  * Copyright(c) 2006-2007, Ext JS, LLC.
20521  *
20522  * Originally Released Under LGPL - original licence link has changed is not relivant.
20523  *
20524  * Fork - LGPL
20525  * <script type="text/javascript">
20526  */
20527  
20528
20529 /**
20530  * @class Roo.menu.DateMenu
20531  * @extends Roo.menu.Menu
20532  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20533  * @constructor
20534  * Creates a new DateMenu
20535  * @param {Object} config Configuration options
20536  */
20537 Roo.menu.DateMenu = function(config){
20538     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20539     this.plain = true;
20540     var di = new Roo.menu.DateItem(config);
20541     this.add(di);
20542     /**
20543      * The {@link Roo.DatePicker} instance for this DateMenu
20544      * @type DatePicker
20545      */
20546     this.picker = di.picker;
20547     /**
20548      * @event select
20549      * @param {DatePicker} picker
20550      * @param {Date} date
20551      */
20552     this.relayEvents(di, ["select"]);
20553     this.on('beforeshow', function(){
20554         if(this.picker){
20555             this.picker.hideMonthPicker(false);
20556         }
20557     }, this);
20558 };
20559 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20560     cls:'x-date-menu'
20561 });/*
20562  * Based on:
20563  * Ext JS Library 1.1.1
20564  * Copyright(c) 2006-2007, Ext JS, LLC.
20565  *
20566  * Originally Released Under LGPL - original licence link has changed is not relivant.
20567  *
20568  * Fork - LGPL
20569  * <script type="text/javascript">
20570  */
20571  
20572
20573 /**
20574  * @class Roo.menu.ColorMenu
20575  * @extends Roo.menu.Menu
20576  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20577  * @constructor
20578  * Creates a new ColorMenu
20579  * @param {Object} config Configuration options
20580  */
20581 Roo.menu.ColorMenu = function(config){
20582     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20583     this.plain = true;
20584     var ci = new Roo.menu.ColorItem(config);
20585     this.add(ci);
20586     /**
20587      * The {@link Roo.ColorPalette} instance for this ColorMenu
20588      * @type ColorPalette
20589      */
20590     this.palette = ci.palette;
20591     /**
20592      * @event select
20593      * @param {ColorPalette} palette
20594      * @param {String} color
20595      */
20596     this.relayEvents(ci, ["select"]);
20597 };
20598 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20599  * Based on:
20600  * Ext JS Library 1.1.1
20601  * Copyright(c) 2006-2007, Ext JS, LLC.
20602  *
20603  * Originally Released Under LGPL - original licence link has changed is not relivant.
20604  *
20605  * Fork - LGPL
20606  * <script type="text/javascript">
20607  */
20608  
20609 /**
20610  * @class Roo.form.Field
20611  * @extends Roo.BoxComponent
20612  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20613  * @constructor
20614  * Creates a new Field
20615  * @param {Object} config Configuration options
20616  */
20617 Roo.form.Field = function(config){
20618     Roo.form.Field.superclass.constructor.call(this, config);
20619 };
20620
20621 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20622     /**
20623      * @cfg {String} fieldLabel Label to use when rendering a form.
20624      */
20625        /**
20626      * @cfg {String} qtip Mouse over tip
20627      */
20628      
20629     /**
20630      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20631      */
20632     invalidClass : "x-form-invalid",
20633     /**
20634      * @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")
20635      */
20636     invalidText : "The value in this field is invalid",
20637     /**
20638      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20639      */
20640     focusClass : "x-form-focus",
20641     /**
20642      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20643       automatic validation (defaults to "keyup").
20644      */
20645     validationEvent : "keyup",
20646     /**
20647      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20648      */
20649     validateOnBlur : true,
20650     /**
20651      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20652      */
20653     validationDelay : 250,
20654     /**
20655      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20656      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20657      */
20658     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
20659     /**
20660      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20661      */
20662     fieldClass : "x-form-field",
20663     /**
20664      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20665      *<pre>
20666 Value         Description
20667 -----------   ----------------------------------------------------------------------
20668 qtip          Display a quick tip when the user hovers over the field
20669 title         Display a default browser title attribute popup
20670 under         Add a block div beneath the field containing the error text
20671 side          Add an error icon to the right of the field with a popup on hover
20672 [element id]  Add the error text directly to the innerHTML of the specified element
20673 </pre>
20674      */
20675     msgTarget : 'qtip',
20676     /**
20677      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20678      */
20679     msgFx : 'normal',
20680
20681     /**
20682      * @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.
20683      */
20684     readOnly : false,
20685
20686     /**
20687      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20688      */
20689     disabled : false,
20690
20691     /**
20692      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20693      */
20694     inputType : undefined,
20695     
20696     /**
20697      * @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).
20698          */
20699         tabIndex : undefined,
20700         
20701     // private
20702     isFormField : true,
20703
20704     // private
20705     hasFocus : false,
20706     /**
20707      * @property {Roo.Element} fieldEl
20708      * Element Containing the rendered Field (with label etc.)
20709      */
20710     /**
20711      * @cfg {Mixed} value A value to initialize this field with.
20712      */
20713     value : undefined,
20714
20715     /**
20716      * @cfg {String} name The field's HTML name attribute.
20717      */
20718     /**
20719      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20720      */
20721     // private
20722     loadedValue : false,
20723      
20724      
20725         // private ??
20726         initComponent : function(){
20727         Roo.form.Field.superclass.initComponent.call(this);
20728         this.addEvents({
20729             /**
20730              * @event focus
20731              * Fires when this field receives input focus.
20732              * @param {Roo.form.Field} this
20733              */
20734             focus : true,
20735             /**
20736              * @event blur
20737              * Fires when this field loses input focus.
20738              * @param {Roo.form.Field} this
20739              */
20740             blur : true,
20741             /**
20742              * @event specialkey
20743              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20744              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20745              * @param {Roo.form.Field} this
20746              * @param {Roo.EventObject} e The event object
20747              */
20748             specialkey : true,
20749             /**
20750              * @event change
20751              * Fires just before the field blurs if the field value has changed.
20752              * @param {Roo.form.Field} this
20753              * @param {Mixed} newValue The new value
20754              * @param {Mixed} oldValue The original value
20755              */
20756             change : true,
20757             /**
20758              * @event invalid
20759              * Fires after the field has been marked as invalid.
20760              * @param {Roo.form.Field} this
20761              * @param {String} msg The validation message
20762              */
20763             invalid : true,
20764             /**
20765              * @event valid
20766              * Fires after the field has been validated with no errors.
20767              * @param {Roo.form.Field} this
20768              */
20769             valid : true,
20770              /**
20771              * @event keyup
20772              * Fires after the key up
20773              * @param {Roo.form.Field} this
20774              * @param {Roo.EventObject}  e The event Object
20775              */
20776             keyup : true
20777         });
20778     },
20779
20780     /**
20781      * Returns the name attribute of the field if available
20782      * @return {String} name The field name
20783      */
20784     getName: function(){
20785          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
20786     },
20787
20788     // private
20789     onRender : function(ct, position){
20790         Roo.form.Field.superclass.onRender.call(this, ct, position);
20791         if(!this.el){
20792             var cfg = this.getAutoCreate();
20793             if(!cfg.name){
20794                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
20795             }
20796             if (!cfg.name.length) {
20797                 delete cfg.name;
20798             }
20799             if(this.inputType){
20800                 cfg.type = this.inputType;
20801             }
20802             this.el = ct.createChild(cfg, position);
20803         }
20804         var type = this.el.dom.type;
20805         if(type){
20806             if(type == 'password'){
20807                 type = 'text';
20808             }
20809             this.el.addClass('x-form-'+type);
20810         }
20811         if(this.readOnly){
20812             this.el.dom.readOnly = true;
20813         }
20814         if(this.tabIndex !== undefined){
20815             this.el.dom.setAttribute('tabIndex', this.tabIndex);
20816         }
20817
20818         this.el.addClass([this.fieldClass, this.cls]);
20819         this.initValue();
20820     },
20821
20822     /**
20823      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
20824      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
20825      * @return {Roo.form.Field} this
20826      */
20827     applyTo : function(target){
20828         this.allowDomMove = false;
20829         this.el = Roo.get(target);
20830         this.render(this.el.dom.parentNode);
20831         return this;
20832     },
20833
20834     // private
20835     initValue : function(){
20836         if(this.value !== undefined){
20837             this.setValue(this.value);
20838         }else if(this.el.dom.value.length > 0){
20839             this.setValue(this.el.dom.value);
20840         }
20841     },
20842
20843     /**
20844      * Returns true if this field has been changed since it was originally loaded and is not disabled.
20845      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
20846      */
20847     isDirty : function() {
20848         if(this.disabled) {
20849             return false;
20850         }
20851         return String(this.getValue()) !== String(this.originalValue);
20852     },
20853
20854     /**
20855      * stores the current value in loadedValue
20856      */
20857     resetHasChanged : function()
20858     {
20859         this.loadedValue = String(this.getValue());
20860     },
20861     /**
20862      * checks the current value against the 'loaded' value.
20863      * Note - will return false if 'resetHasChanged' has not been called first.
20864      */
20865     hasChanged : function()
20866     {
20867         if(this.disabled || this.readOnly) {
20868             return false;
20869         }
20870         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
20871     },
20872     
20873     
20874     
20875     // private
20876     afterRender : function(){
20877         Roo.form.Field.superclass.afterRender.call(this);
20878         this.initEvents();
20879     },
20880
20881     // private
20882     fireKey : function(e){
20883         //Roo.log('field ' + e.getKey());
20884         if(e.isNavKeyPress()){
20885             this.fireEvent("specialkey", this, e);
20886         }
20887     },
20888
20889     /**
20890      * Resets the current field value to the originally loaded value and clears any validation messages
20891      */
20892     reset : function(){
20893         this.setValue(this.resetValue);
20894         this.clearInvalid();
20895     },
20896
20897     // private
20898     initEvents : function(){
20899         // safari killled keypress - so keydown is now used..
20900         this.el.on("keydown" , this.fireKey,  this);
20901         this.el.on("focus", this.onFocus,  this);
20902         this.el.on("blur", this.onBlur,  this);
20903         this.el.relayEvent('keyup', this);
20904
20905         // reference to original value for reset
20906         this.originalValue = this.getValue();
20907         this.resetValue =  this.getValue();
20908     },
20909
20910     // private
20911     onFocus : function(){
20912         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20913             this.el.addClass(this.focusClass);
20914         }
20915         if(!this.hasFocus){
20916             this.hasFocus = true;
20917             this.startValue = this.getValue();
20918             this.fireEvent("focus", this);
20919         }
20920     },
20921
20922     beforeBlur : Roo.emptyFn,
20923
20924     // private
20925     onBlur : function(){
20926         this.beforeBlur();
20927         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20928             this.el.removeClass(this.focusClass);
20929         }
20930         this.hasFocus = false;
20931         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
20932             this.validate();
20933         }
20934         var v = this.getValue();
20935         if(String(v) !== String(this.startValue)){
20936             this.fireEvent('change', this, v, this.startValue);
20937         }
20938         this.fireEvent("blur", this);
20939     },
20940
20941     /**
20942      * Returns whether or not the field value is currently valid
20943      * @param {Boolean} preventMark True to disable marking the field invalid
20944      * @return {Boolean} True if the value is valid, else false
20945      */
20946     isValid : function(preventMark){
20947         if(this.disabled){
20948             return true;
20949         }
20950         var restore = this.preventMark;
20951         this.preventMark = preventMark === true;
20952         var v = this.validateValue(this.processValue(this.getRawValue()));
20953         this.preventMark = restore;
20954         return v;
20955     },
20956
20957     /**
20958      * Validates the field value
20959      * @return {Boolean} True if the value is valid, else false
20960      */
20961     validate : function(){
20962         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
20963             this.clearInvalid();
20964             return true;
20965         }
20966         return false;
20967     },
20968
20969     processValue : function(value){
20970         return value;
20971     },
20972
20973     // private
20974     // Subclasses should provide the validation implementation by overriding this
20975     validateValue : function(value){
20976         return true;
20977     },
20978
20979     /**
20980      * Mark this field as invalid
20981      * @param {String} msg The validation message
20982      */
20983     markInvalid : function(msg){
20984         if(!this.rendered || this.preventMark){ // not rendered
20985             return;
20986         }
20987         
20988         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
20989         
20990         obj.el.addClass(this.invalidClass);
20991         msg = msg || this.invalidText;
20992         switch(this.msgTarget){
20993             case 'qtip':
20994                 obj.el.dom.qtip = msg;
20995                 obj.el.dom.qclass = 'x-form-invalid-tip';
20996                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
20997                     Roo.QuickTips.enable();
20998                 }
20999                 break;
21000             case 'title':
21001                 this.el.dom.title = msg;
21002                 break;
21003             case 'under':
21004                 if(!this.errorEl){
21005                     var elp = this.el.findParent('.x-form-element', 5, true);
21006                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21007                     this.errorEl.setWidth(elp.getWidth(true)-20);
21008                 }
21009                 this.errorEl.update(msg);
21010                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21011                 break;
21012             case 'side':
21013                 if(!this.errorIcon){
21014                     var elp = this.el.findParent('.x-form-element', 5, true);
21015                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21016                 }
21017                 this.alignErrorIcon();
21018                 this.errorIcon.dom.qtip = msg;
21019                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21020                 this.errorIcon.show();
21021                 this.on('resize', this.alignErrorIcon, this);
21022                 break;
21023             default:
21024                 var t = Roo.getDom(this.msgTarget);
21025                 t.innerHTML = msg;
21026                 t.style.display = this.msgDisplay;
21027                 break;
21028         }
21029         this.fireEvent('invalid', this, msg);
21030     },
21031
21032     // private
21033     alignErrorIcon : function(){
21034         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21035     },
21036
21037     /**
21038      * Clear any invalid styles/messages for this field
21039      */
21040     clearInvalid : function(){
21041         if(!this.rendered || this.preventMark){ // not rendered
21042             return;
21043         }
21044         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
21045         
21046         obj.el.removeClass(this.invalidClass);
21047         switch(this.msgTarget){
21048             case 'qtip':
21049                 obj.el.dom.qtip = '';
21050                 break;
21051             case 'title':
21052                 this.el.dom.title = '';
21053                 break;
21054             case 'under':
21055                 if(this.errorEl){
21056                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21057                 }
21058                 break;
21059             case 'side':
21060                 if(this.errorIcon){
21061                     this.errorIcon.dom.qtip = '';
21062                     this.errorIcon.hide();
21063                     this.un('resize', this.alignErrorIcon, this);
21064                 }
21065                 break;
21066             default:
21067                 var t = Roo.getDom(this.msgTarget);
21068                 t.innerHTML = '';
21069                 t.style.display = 'none';
21070                 break;
21071         }
21072         this.fireEvent('valid', this);
21073     },
21074
21075     /**
21076      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21077      * @return {Mixed} value The field value
21078      */
21079     getRawValue : function(){
21080         var v = this.el.getValue();
21081         
21082         return v;
21083     },
21084
21085     /**
21086      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21087      * @return {Mixed} value The field value
21088      */
21089     getValue : function(){
21090         var v = this.el.getValue();
21091          
21092         return v;
21093     },
21094
21095     /**
21096      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21097      * @param {Mixed} value The value to set
21098      */
21099     setRawValue : function(v){
21100         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21101     },
21102
21103     /**
21104      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21105      * @param {Mixed} value The value to set
21106      */
21107     setValue : function(v){
21108         this.value = v;
21109         if(this.rendered){
21110             this.el.dom.value = (v === null || v === undefined ? '' : v);
21111              this.validate();
21112         }
21113     },
21114
21115     adjustSize : function(w, h){
21116         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21117         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21118         return s;
21119     },
21120
21121     adjustWidth : function(tag, w){
21122         tag = tag.toLowerCase();
21123         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21124             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21125                 if(tag == 'input'){
21126                     return w + 2;
21127                 }
21128                 if(tag == 'textarea'){
21129                     return w-2;
21130                 }
21131             }else if(Roo.isOpera){
21132                 if(tag == 'input'){
21133                     return w + 2;
21134                 }
21135                 if(tag == 'textarea'){
21136                     return w-2;
21137                 }
21138             }
21139         }
21140         return w;
21141     }
21142 });
21143
21144
21145 // anything other than normal should be considered experimental
21146 Roo.form.Field.msgFx = {
21147     normal : {
21148         show: function(msgEl, f){
21149             msgEl.setDisplayed('block');
21150         },
21151
21152         hide : function(msgEl, f){
21153             msgEl.setDisplayed(false).update('');
21154         }
21155     },
21156
21157     slide : {
21158         show: function(msgEl, f){
21159             msgEl.slideIn('t', {stopFx:true});
21160         },
21161
21162         hide : function(msgEl, f){
21163             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21164         }
21165     },
21166
21167     slideRight : {
21168         show: function(msgEl, f){
21169             msgEl.fixDisplay();
21170             msgEl.alignTo(f.el, 'tl-tr');
21171             msgEl.slideIn('l', {stopFx:true});
21172         },
21173
21174         hide : function(msgEl, f){
21175             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21176         }
21177     }
21178 };/*
21179  * Based on:
21180  * Ext JS Library 1.1.1
21181  * Copyright(c) 2006-2007, Ext JS, LLC.
21182  *
21183  * Originally Released Under LGPL - original licence link has changed is not relivant.
21184  *
21185  * Fork - LGPL
21186  * <script type="text/javascript">
21187  */
21188  
21189
21190 /**
21191  * @class Roo.form.TextField
21192  * @extends Roo.form.Field
21193  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21194  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21195  * @constructor
21196  * Creates a new TextField
21197  * @param {Object} config Configuration options
21198  */
21199 Roo.form.TextField = function(config){
21200     Roo.form.TextField.superclass.constructor.call(this, config);
21201     this.addEvents({
21202         /**
21203          * @event autosize
21204          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21205          * according to the default logic, but this event provides a hook for the developer to apply additional
21206          * logic at runtime to resize the field if needed.
21207              * @param {Roo.form.Field} this This text field
21208              * @param {Number} width The new field width
21209              */
21210         autosize : true
21211     });
21212 };
21213
21214 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21215     /**
21216      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21217      */
21218     grow : false,
21219     /**
21220      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21221      */
21222     growMin : 30,
21223     /**
21224      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21225      */
21226     growMax : 800,
21227     /**
21228      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21229      */
21230     vtype : null,
21231     /**
21232      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21233      */
21234     maskRe : null,
21235     /**
21236      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21237      */
21238     disableKeyFilter : false,
21239     /**
21240      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21241      */
21242     allowBlank : true,
21243     /**
21244      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21245      */
21246     minLength : 0,
21247     /**
21248      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21249      */
21250     maxLength : Number.MAX_VALUE,
21251     /**
21252      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21253      */
21254     minLengthText : "The minimum length for this field is {0}",
21255     /**
21256      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21257      */
21258     maxLengthText : "The maximum length for this field is {0}",
21259     /**
21260      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21261      */
21262     selectOnFocus : false,
21263     /**
21264      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21265      */
21266     blankText : "This field is required",
21267     /**
21268      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21269      * If available, this function will be called only after the basic validators all return true, and will be passed the
21270      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21271      */
21272     validator : null,
21273     /**
21274      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21275      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21276      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21277      */
21278     regex : null,
21279     /**
21280      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21281      */
21282     regexText : "",
21283     /**
21284      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
21285      */
21286     emptyText : null,
21287    
21288
21289     // private
21290     initEvents : function()
21291     {
21292         if (this.emptyText) {
21293             this.el.attr('placeholder', this.emptyText);
21294         }
21295         
21296         Roo.form.TextField.superclass.initEvents.call(this);
21297         if(this.validationEvent == 'keyup'){
21298             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21299             this.el.on('keyup', this.filterValidation, this);
21300         }
21301         else if(this.validationEvent !== false){
21302             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21303         }
21304         
21305         if(this.selectOnFocus){
21306             this.on("focus", this.preFocus, this);
21307             
21308         }
21309         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21310             this.el.on("keypress", this.filterKeys, this);
21311         }
21312         if(this.grow){
21313             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21314             this.el.on("click", this.autoSize,  this);
21315         }
21316         if(this.el.is('input[type=password]') && Roo.isSafari){
21317             this.el.on('keydown', this.SafariOnKeyDown, this);
21318         }
21319     },
21320
21321     processValue : function(value){
21322         if(this.stripCharsRe){
21323             var newValue = value.replace(this.stripCharsRe, '');
21324             if(newValue !== value){
21325                 this.setRawValue(newValue);
21326                 return newValue;
21327             }
21328         }
21329         return value;
21330     },
21331
21332     filterValidation : function(e){
21333         if(!e.isNavKeyPress()){
21334             this.validationTask.delay(this.validationDelay);
21335         }
21336     },
21337
21338     // private
21339     onKeyUp : function(e){
21340         if(!e.isNavKeyPress()){
21341             this.autoSize();
21342         }
21343     },
21344
21345     /**
21346      * Resets the current field value to the originally-loaded value and clears any validation messages.
21347      *  
21348      */
21349     reset : function(){
21350         Roo.form.TextField.superclass.reset.call(this);
21351        
21352     },
21353
21354     
21355     // private
21356     preFocus : function(){
21357         
21358         if(this.selectOnFocus){
21359             this.el.dom.select();
21360         }
21361     },
21362
21363     
21364     // private
21365     filterKeys : function(e){
21366         var k = e.getKey();
21367         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21368             return;
21369         }
21370         var c = e.getCharCode(), cc = String.fromCharCode(c);
21371         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21372             return;
21373         }
21374         if(!this.maskRe.test(cc)){
21375             e.stopEvent();
21376         }
21377     },
21378
21379     setValue : function(v){
21380         
21381         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21382         
21383         this.autoSize();
21384     },
21385
21386     /**
21387      * Validates a value according to the field's validation rules and marks the field as invalid
21388      * if the validation fails
21389      * @param {Mixed} value The value to validate
21390      * @return {Boolean} True if the value is valid, else false
21391      */
21392     validateValue : function(value){
21393         if(value.length < 1)  { // if it's blank
21394              if(this.allowBlank){
21395                 this.clearInvalid();
21396                 return true;
21397              }else{
21398                 this.markInvalid(this.blankText);
21399                 return false;
21400              }
21401         }
21402         if(value.length < this.minLength){
21403             this.markInvalid(String.format(this.minLengthText, this.minLength));
21404             return false;
21405         }
21406         if(value.length > this.maxLength){
21407             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21408             return false;
21409         }
21410         if(this.vtype){
21411             var vt = Roo.form.VTypes;
21412             if(!vt[this.vtype](value, this)){
21413                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21414                 return false;
21415             }
21416         }
21417         if(typeof this.validator == "function"){
21418             var msg = this.validator(value);
21419             if(msg !== true){
21420                 this.markInvalid(msg);
21421                 return false;
21422             }
21423         }
21424         if(this.regex && !this.regex.test(value)){
21425             this.markInvalid(this.regexText);
21426             return false;
21427         }
21428         return true;
21429     },
21430
21431     /**
21432      * Selects text in this field
21433      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21434      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21435      */
21436     selectText : function(start, end){
21437         var v = this.getRawValue();
21438         if(v.length > 0){
21439             start = start === undefined ? 0 : start;
21440             end = end === undefined ? v.length : end;
21441             var d = this.el.dom;
21442             if(d.setSelectionRange){
21443                 d.setSelectionRange(start, end);
21444             }else if(d.createTextRange){
21445                 var range = d.createTextRange();
21446                 range.moveStart("character", start);
21447                 range.moveEnd("character", v.length-end);
21448                 range.select();
21449             }
21450         }
21451     },
21452
21453     /**
21454      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21455      * This only takes effect if grow = true, and fires the autosize event.
21456      */
21457     autoSize : function(){
21458         if(!this.grow || !this.rendered){
21459             return;
21460         }
21461         if(!this.metrics){
21462             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21463         }
21464         var el = this.el;
21465         var v = el.dom.value;
21466         var d = document.createElement('div');
21467         d.appendChild(document.createTextNode(v));
21468         v = d.innerHTML;
21469         d = null;
21470         v += "&#160;";
21471         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21472         this.el.setWidth(w);
21473         this.fireEvent("autosize", this, w);
21474     },
21475     
21476     // private
21477     SafariOnKeyDown : function(event)
21478     {
21479         // this is a workaround for a password hang bug on chrome/ webkit.
21480         
21481         var isSelectAll = false;
21482         
21483         if(this.el.dom.selectionEnd > 0){
21484             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
21485         }
21486         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
21487             event.preventDefault();
21488             this.setValue('');
21489             return;
21490         }
21491         
21492         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
21493             
21494             event.preventDefault();
21495             // this is very hacky as keydown always get's upper case.
21496             
21497             var cc = String.fromCharCode(event.getCharCode());
21498             
21499             
21500             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
21501             
21502         }
21503         
21504         
21505     }
21506 });/*
21507  * Based on:
21508  * Ext JS Library 1.1.1
21509  * Copyright(c) 2006-2007, Ext JS, LLC.
21510  *
21511  * Originally Released Under LGPL - original licence link has changed is not relivant.
21512  *
21513  * Fork - LGPL
21514  * <script type="text/javascript">
21515  */
21516  
21517 /**
21518  * @class Roo.form.Hidden
21519  * @extends Roo.form.TextField
21520  * Simple Hidden element used on forms 
21521  * 
21522  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21523  * 
21524  * @constructor
21525  * Creates a new Hidden form element.
21526  * @param {Object} config Configuration options
21527  */
21528
21529
21530
21531 // easy hidden field...
21532 Roo.form.Hidden = function(config){
21533     Roo.form.Hidden.superclass.constructor.call(this, config);
21534 };
21535   
21536 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21537     fieldLabel:      '',
21538     inputType:      'hidden',
21539     width:          50,
21540     allowBlank:     true,
21541     labelSeparator: '',
21542     hidden:         true,
21543     itemCls :       'x-form-item-display-none'
21544
21545
21546 });
21547
21548
21549 /*
21550  * Based on:
21551  * Ext JS Library 1.1.1
21552  * Copyright(c) 2006-2007, Ext JS, LLC.
21553  *
21554  * Originally Released Under LGPL - original licence link has changed is not relivant.
21555  *
21556  * Fork - LGPL
21557  * <script type="text/javascript">
21558  */
21559  
21560 /**
21561  * @class Roo.form.TriggerField
21562  * @extends Roo.form.TextField
21563  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21564  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21565  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21566  * for which you can provide a custom implementation.  For example:
21567  * <pre><code>
21568 var trigger = new Roo.form.TriggerField();
21569 trigger.onTriggerClick = myTriggerFn;
21570 trigger.applyTo('my-field');
21571 </code></pre>
21572  *
21573  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21574  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21575  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21576  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21577  * @constructor
21578  * Create a new TriggerField.
21579  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21580  * to the base TextField)
21581  */
21582 Roo.form.TriggerField = function(config){
21583     this.mimicing = false;
21584     Roo.form.TriggerField.superclass.constructor.call(this, config);
21585 };
21586
21587 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21588     /**
21589      * @cfg {String} triggerClass A CSS class to apply to the trigger
21590      */
21591     /**
21592      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21593      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21594      */
21595     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
21596     /**
21597      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21598      */
21599     hideTrigger:false,
21600
21601     /** @cfg {Boolean} grow @hide */
21602     /** @cfg {Number} growMin @hide */
21603     /** @cfg {Number} growMax @hide */
21604
21605     /**
21606      * @hide 
21607      * @method
21608      */
21609     autoSize: Roo.emptyFn,
21610     // private
21611     monitorTab : true,
21612     // private
21613     deferHeight : true,
21614
21615     
21616     actionMode : 'wrap',
21617     // private
21618     onResize : function(w, h){
21619         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21620         if(typeof w == 'number'){
21621             var x = w - this.trigger.getWidth();
21622             this.el.setWidth(this.adjustWidth('input', x));
21623             this.trigger.setStyle('left', x+'px');
21624         }
21625     },
21626
21627     // private
21628     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21629
21630     // private
21631     getResizeEl : function(){
21632         return this.wrap;
21633     },
21634
21635     // private
21636     getPositionEl : function(){
21637         return this.wrap;
21638     },
21639
21640     // private
21641     alignErrorIcon : function(){
21642         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21643     },
21644
21645     // private
21646     onRender : function(ct, position){
21647         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21648         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21649         this.trigger = this.wrap.createChild(this.triggerConfig ||
21650                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21651         if(this.hideTrigger){
21652             this.trigger.setDisplayed(false);
21653         }
21654         this.initTrigger();
21655         if(!this.width){
21656             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21657         }
21658     },
21659
21660     // private
21661     initTrigger : function(){
21662         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21663         this.trigger.addClassOnOver('x-form-trigger-over');
21664         this.trigger.addClassOnClick('x-form-trigger-click');
21665     },
21666
21667     // private
21668     onDestroy : function(){
21669         if(this.trigger){
21670             this.trigger.removeAllListeners();
21671             this.trigger.remove();
21672         }
21673         if(this.wrap){
21674             this.wrap.remove();
21675         }
21676         Roo.form.TriggerField.superclass.onDestroy.call(this);
21677     },
21678
21679     // private
21680     onFocus : function(){
21681         Roo.form.TriggerField.superclass.onFocus.call(this);
21682         if(!this.mimicing){
21683             this.wrap.addClass('x-trigger-wrap-focus');
21684             this.mimicing = true;
21685             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21686             if(this.monitorTab){
21687                 this.el.on("keydown", this.checkTab, this);
21688             }
21689         }
21690     },
21691
21692     // private
21693     checkTab : function(e){
21694         if(e.getKey() == e.TAB){
21695             this.triggerBlur();
21696         }
21697     },
21698
21699     // private
21700     onBlur : function(){
21701         // do nothing
21702     },
21703
21704     // private
21705     mimicBlur : function(e, t){
21706         if(!this.wrap.contains(t) && this.validateBlur()){
21707             this.triggerBlur();
21708         }
21709     },
21710
21711     // private
21712     triggerBlur : function(){
21713         this.mimicing = false;
21714         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21715         if(this.monitorTab){
21716             this.el.un("keydown", this.checkTab, this);
21717         }
21718         this.wrap.removeClass('x-trigger-wrap-focus');
21719         Roo.form.TriggerField.superclass.onBlur.call(this);
21720     },
21721
21722     // private
21723     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21724     validateBlur : function(e, t){
21725         return true;
21726     },
21727
21728     // private
21729     onDisable : function(){
21730         Roo.form.TriggerField.superclass.onDisable.call(this);
21731         if(this.wrap){
21732             this.wrap.addClass('x-item-disabled');
21733         }
21734     },
21735
21736     // private
21737     onEnable : function(){
21738         Roo.form.TriggerField.superclass.onEnable.call(this);
21739         if(this.wrap){
21740             this.wrap.removeClass('x-item-disabled');
21741         }
21742     },
21743
21744     // private
21745     onShow : function(){
21746         var ae = this.getActionEl();
21747         
21748         if(ae){
21749             ae.dom.style.display = '';
21750             ae.dom.style.visibility = 'visible';
21751         }
21752     },
21753
21754     // private
21755     
21756     onHide : function(){
21757         var ae = this.getActionEl();
21758         ae.dom.style.display = 'none';
21759     },
21760
21761     /**
21762      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
21763      * by an implementing function.
21764      * @method
21765      * @param {EventObject} e
21766      */
21767     onTriggerClick : Roo.emptyFn
21768 });
21769
21770 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
21771 // to be extended by an implementing class.  For an example of implementing this class, see the custom
21772 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21773 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21774     initComponent : function(){
21775         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21776
21777         this.triggerConfig = {
21778             tag:'span', cls:'x-form-twin-triggers', cn:[
21779             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21780             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21781         ]};
21782     },
21783
21784     getTrigger : function(index){
21785         return this.triggers[index];
21786     },
21787
21788     initTrigger : function(){
21789         var ts = this.trigger.select('.x-form-trigger', true);
21790         this.wrap.setStyle('overflow', 'hidden');
21791         var triggerField = this;
21792         ts.each(function(t, all, index){
21793             t.hide = function(){
21794                 var w = triggerField.wrap.getWidth();
21795                 this.dom.style.display = 'none';
21796                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21797             };
21798             t.show = function(){
21799                 var w = triggerField.wrap.getWidth();
21800                 this.dom.style.display = '';
21801                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21802             };
21803             var triggerIndex = 'Trigger'+(index+1);
21804
21805             if(this['hide'+triggerIndex]){
21806                 t.dom.style.display = 'none';
21807             }
21808             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
21809             t.addClassOnOver('x-form-trigger-over');
21810             t.addClassOnClick('x-form-trigger-click');
21811         }, this);
21812         this.triggers = ts.elements;
21813     },
21814
21815     onTrigger1Click : Roo.emptyFn,
21816     onTrigger2Click : Roo.emptyFn
21817 });/*
21818  * Based on:
21819  * Ext JS Library 1.1.1
21820  * Copyright(c) 2006-2007, Ext JS, LLC.
21821  *
21822  * Originally Released Under LGPL - original licence link has changed is not relivant.
21823  *
21824  * Fork - LGPL
21825  * <script type="text/javascript">
21826  */
21827  
21828 /**
21829  * @class Roo.form.TextArea
21830  * @extends Roo.form.TextField
21831  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
21832  * support for auto-sizing.
21833  * @constructor
21834  * Creates a new TextArea
21835  * @param {Object} config Configuration options
21836  */
21837 Roo.form.TextArea = function(config){
21838     Roo.form.TextArea.superclass.constructor.call(this, config);
21839     // these are provided exchanges for backwards compat
21840     // minHeight/maxHeight were replaced by growMin/growMax to be
21841     // compatible with TextField growing config values
21842     if(this.minHeight !== undefined){
21843         this.growMin = this.minHeight;
21844     }
21845     if(this.maxHeight !== undefined){
21846         this.growMax = this.maxHeight;
21847     }
21848 };
21849
21850 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
21851     /**
21852      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
21853      */
21854     growMin : 60,
21855     /**
21856      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
21857      */
21858     growMax: 1000,
21859     /**
21860      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
21861      * in the field (equivalent to setting overflow: hidden, defaults to false)
21862      */
21863     preventScrollbars: false,
21864     /**
21865      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21866      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
21867      */
21868
21869     // private
21870     onRender : function(ct, position){
21871         if(!this.el){
21872             this.defaultAutoCreate = {
21873                 tag: "textarea",
21874                 style:"width:300px;height:60px;",
21875                 autocomplete: "new-password"
21876             };
21877         }
21878         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
21879         if(this.grow){
21880             this.textSizeEl = Roo.DomHelper.append(document.body, {
21881                 tag: "pre", cls: "x-form-grow-sizer"
21882             });
21883             if(this.preventScrollbars){
21884                 this.el.setStyle("overflow", "hidden");
21885             }
21886             this.el.setHeight(this.growMin);
21887         }
21888     },
21889
21890     onDestroy : function(){
21891         if(this.textSizeEl){
21892             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
21893         }
21894         Roo.form.TextArea.superclass.onDestroy.call(this);
21895     },
21896
21897     // private
21898     onKeyUp : function(e){
21899         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
21900             this.autoSize();
21901         }
21902     },
21903
21904     /**
21905      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
21906      * This only takes effect if grow = true, and fires the autosize event if the height changes.
21907      */
21908     autoSize : function(){
21909         if(!this.grow || !this.textSizeEl){
21910             return;
21911         }
21912         var el = this.el;
21913         var v = el.dom.value;
21914         var ts = this.textSizeEl;
21915
21916         ts.innerHTML = '';
21917         ts.appendChild(document.createTextNode(v));
21918         v = ts.innerHTML;
21919
21920         Roo.fly(ts).setWidth(this.el.getWidth());
21921         if(v.length < 1){
21922             v = "&#160;&#160;";
21923         }else{
21924             if(Roo.isIE){
21925                 v = v.replace(/\n/g, '<p>&#160;</p>');
21926             }
21927             v += "&#160;\n&#160;";
21928         }
21929         ts.innerHTML = v;
21930         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
21931         if(h != this.lastHeight){
21932             this.lastHeight = h;
21933             this.el.setHeight(h);
21934             this.fireEvent("autosize", this, h);
21935         }
21936     }
21937 });/*
21938  * Based on:
21939  * Ext JS Library 1.1.1
21940  * Copyright(c) 2006-2007, Ext JS, LLC.
21941  *
21942  * Originally Released Under LGPL - original licence link has changed is not relivant.
21943  *
21944  * Fork - LGPL
21945  * <script type="text/javascript">
21946  */
21947  
21948
21949 /**
21950  * @class Roo.form.NumberField
21951  * @extends Roo.form.TextField
21952  * Numeric text field that provides automatic keystroke filtering and numeric validation.
21953  * @constructor
21954  * Creates a new NumberField
21955  * @param {Object} config Configuration options
21956  */
21957 Roo.form.NumberField = function(config){
21958     Roo.form.NumberField.superclass.constructor.call(this, config);
21959 };
21960
21961 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
21962     /**
21963      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
21964      */
21965     fieldClass: "x-form-field x-form-num-field",
21966     /**
21967      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
21968      */
21969     allowDecimals : true,
21970     /**
21971      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
21972      */
21973     decimalSeparator : ".",
21974     /**
21975      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
21976      */
21977     decimalPrecision : 2,
21978     /**
21979      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
21980      */
21981     allowNegative : true,
21982     /**
21983      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
21984      */
21985     minValue : Number.NEGATIVE_INFINITY,
21986     /**
21987      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
21988      */
21989     maxValue : Number.MAX_VALUE,
21990     /**
21991      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
21992      */
21993     minText : "The minimum value for this field is {0}",
21994     /**
21995      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
21996      */
21997     maxText : "The maximum value for this field is {0}",
21998     /**
21999      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22000      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22001      */
22002     nanText : "{0} is not a valid number",
22003
22004     // private
22005     initEvents : function(){
22006         Roo.form.NumberField.superclass.initEvents.call(this);
22007         var allowed = "0123456789";
22008         if(this.allowDecimals){
22009             allowed += this.decimalSeparator;
22010         }
22011         if(this.allowNegative){
22012             allowed += "-";
22013         }
22014         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22015         var keyPress = function(e){
22016             var k = e.getKey();
22017             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22018                 return;
22019             }
22020             var c = e.getCharCode();
22021             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22022                 e.stopEvent();
22023             }
22024         };
22025         this.el.on("keypress", keyPress, this);
22026     },
22027
22028     // private
22029     validateValue : function(value){
22030         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22031             return false;
22032         }
22033         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22034              return true;
22035         }
22036         var num = this.parseValue(value);
22037         if(isNaN(num)){
22038             this.markInvalid(String.format(this.nanText, value));
22039             return false;
22040         }
22041         if(num < this.minValue){
22042             this.markInvalid(String.format(this.minText, this.minValue));
22043             return false;
22044         }
22045         if(num > this.maxValue){
22046             this.markInvalid(String.format(this.maxText, this.maxValue));
22047             return false;
22048         }
22049         return true;
22050     },
22051
22052     getValue : function(){
22053         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22054     },
22055
22056     // private
22057     parseValue : function(value){
22058         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22059         return isNaN(value) ? '' : value;
22060     },
22061
22062     // private
22063     fixPrecision : function(value){
22064         var nan = isNaN(value);
22065         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22066             return nan ? '' : value;
22067         }
22068         return parseFloat(value).toFixed(this.decimalPrecision);
22069     },
22070
22071     setValue : function(v){
22072         v = this.fixPrecision(v);
22073         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22074     },
22075
22076     // private
22077     decimalPrecisionFcn : function(v){
22078         return Math.floor(v);
22079     },
22080
22081     beforeBlur : function(){
22082         var v = this.parseValue(this.getRawValue());
22083         if(v){
22084             this.setValue(v);
22085         }
22086     }
22087 });/*
22088  * Based on:
22089  * Ext JS Library 1.1.1
22090  * Copyright(c) 2006-2007, Ext JS, LLC.
22091  *
22092  * Originally Released Under LGPL - original licence link has changed is not relivant.
22093  *
22094  * Fork - LGPL
22095  * <script type="text/javascript">
22096  */
22097  
22098 /**
22099  * @class Roo.form.DateField
22100  * @extends Roo.form.TriggerField
22101  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22102 * @constructor
22103 * Create a new DateField
22104 * @param {Object} config
22105  */
22106 Roo.form.DateField = function(config){
22107     Roo.form.DateField.superclass.constructor.call(this, config);
22108     
22109       this.addEvents({
22110          
22111         /**
22112          * @event select
22113          * Fires when a date is selected
22114              * @param {Roo.form.DateField} combo This combo box
22115              * @param {Date} date The date selected
22116              */
22117         'select' : true
22118          
22119     });
22120     
22121     
22122     if(typeof this.minValue == "string") {
22123         this.minValue = this.parseDate(this.minValue);
22124     }
22125     if(typeof this.maxValue == "string") {
22126         this.maxValue = this.parseDate(this.maxValue);
22127     }
22128     this.ddMatch = null;
22129     if(this.disabledDates){
22130         var dd = this.disabledDates;
22131         var re = "(?:";
22132         for(var i = 0; i < dd.length; i++){
22133             re += dd[i];
22134             if(i != dd.length-1) {
22135                 re += "|";
22136             }
22137         }
22138         this.ddMatch = new RegExp(re + ")");
22139     }
22140 };
22141
22142 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22143     /**
22144      * @cfg {String} format
22145      * The default date format string which can be overriden for localization support.  The format must be
22146      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22147      */
22148     format : "m/d/y",
22149     /**
22150      * @cfg {String} altFormats
22151      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22152      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22153      */
22154     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22155     /**
22156      * @cfg {Array} disabledDays
22157      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22158      */
22159     disabledDays : null,
22160     /**
22161      * @cfg {String} disabledDaysText
22162      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22163      */
22164     disabledDaysText : "Disabled",
22165     /**
22166      * @cfg {Array} disabledDates
22167      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22168      * expression so they are very powerful. Some examples:
22169      * <ul>
22170      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22171      * <li>["03/08", "09/16"] would disable those days for every year</li>
22172      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22173      * <li>["03/../2006"] would disable every day in March 2006</li>
22174      * <li>["^03"] would disable every day in every March</li>
22175      * </ul>
22176      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22177      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22178      */
22179     disabledDates : null,
22180     /**
22181      * @cfg {String} disabledDatesText
22182      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22183      */
22184     disabledDatesText : "Disabled",
22185     /**
22186      * @cfg {Date/String} minValue
22187      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22188      * valid format (defaults to null).
22189      */
22190     minValue : null,
22191     /**
22192      * @cfg {Date/String} maxValue
22193      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22194      * valid format (defaults to null).
22195      */
22196     maxValue : null,
22197     /**
22198      * @cfg {String} minText
22199      * The error text to display when the date in the cell is before minValue (defaults to
22200      * 'The date in this field must be after {minValue}').
22201      */
22202     minText : "The date in this field must be equal to or after {0}",
22203     /**
22204      * @cfg {String} maxText
22205      * The error text to display when the date in the cell is after maxValue (defaults to
22206      * 'The date in this field must be before {maxValue}').
22207      */
22208     maxText : "The date in this field must be equal to or before {0}",
22209     /**
22210      * @cfg {String} invalidText
22211      * The error text to display when the date in the field is invalid (defaults to
22212      * '{value} is not a valid date - it must be in the format {format}').
22213      */
22214     invalidText : "{0} is not a valid date - it must be in the format {1}",
22215     /**
22216      * @cfg {String} triggerClass
22217      * An additional CSS class used to style the trigger button.  The trigger will always get the
22218      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22219      * which displays a calendar icon).
22220      */
22221     triggerClass : 'x-form-date-trigger',
22222     
22223
22224     /**
22225      * @cfg {Boolean} useIso
22226      * if enabled, then the date field will use a hidden field to store the 
22227      * real value as iso formated date. default (false)
22228      */ 
22229     useIso : false,
22230     /**
22231      * @cfg {String/Object} autoCreate
22232      * A DomHelper element spec, or true for a default element spec (defaults to
22233      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22234      */ 
22235     // private
22236     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22237     
22238     // private
22239     hiddenField: false,
22240     
22241     onRender : function(ct, position)
22242     {
22243         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22244         if (this.useIso) {
22245             //this.el.dom.removeAttribute('name'); 
22246             Roo.log("Changing name?");
22247             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
22248             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22249                     'before', true);
22250             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22251             // prevent input submission
22252             this.hiddenName = this.name;
22253         }
22254             
22255             
22256     },
22257     
22258     // private
22259     validateValue : function(value)
22260     {
22261         value = this.formatDate(value);
22262         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22263             Roo.log('super failed');
22264             return false;
22265         }
22266         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22267              return true;
22268         }
22269         var svalue = value;
22270         value = this.parseDate(value);
22271         if(!value){
22272             Roo.log('parse date failed' + svalue);
22273             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22274             return false;
22275         }
22276         var time = value.getTime();
22277         if(this.minValue && time < this.minValue.getTime()){
22278             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22279             return false;
22280         }
22281         if(this.maxValue && time > this.maxValue.getTime()){
22282             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22283             return false;
22284         }
22285         if(this.disabledDays){
22286             var day = value.getDay();
22287             for(var i = 0; i < this.disabledDays.length; i++) {
22288                 if(day === this.disabledDays[i]){
22289                     this.markInvalid(this.disabledDaysText);
22290                     return false;
22291                 }
22292             }
22293         }
22294         var fvalue = this.formatDate(value);
22295         if(this.ddMatch && this.ddMatch.test(fvalue)){
22296             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22297             return false;
22298         }
22299         return true;
22300     },
22301
22302     // private
22303     // Provides logic to override the default TriggerField.validateBlur which just returns true
22304     validateBlur : function(){
22305         return !this.menu || !this.menu.isVisible();
22306     },
22307     
22308     getName: function()
22309     {
22310         // returns hidden if it's set..
22311         if (!this.rendered) {return ''};
22312         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
22313         
22314     },
22315
22316     /**
22317      * Returns the current date value of the date field.
22318      * @return {Date} The date value
22319      */
22320     getValue : function(){
22321         
22322         return  this.hiddenField ?
22323                 this.hiddenField.value :
22324                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22325     },
22326
22327     /**
22328      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22329      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22330      * (the default format used is "m/d/y").
22331      * <br />Usage:
22332      * <pre><code>
22333 //All of these calls set the same date value (May 4, 2006)
22334
22335 //Pass a date object:
22336 var dt = new Date('5/4/06');
22337 dateField.setValue(dt);
22338
22339 //Pass a date string (default format):
22340 dateField.setValue('5/4/06');
22341
22342 //Pass a date string (custom format):
22343 dateField.format = 'Y-m-d';
22344 dateField.setValue('2006-5-4');
22345 </code></pre>
22346      * @param {String/Date} date The date or valid date string
22347      */
22348     setValue : function(date){
22349         if (this.hiddenField) {
22350             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22351         }
22352         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22353         // make sure the value field is always stored as a date..
22354         this.value = this.parseDate(date);
22355         
22356         
22357     },
22358
22359     // private
22360     parseDate : function(value){
22361         if(!value || value instanceof Date){
22362             return value;
22363         }
22364         var v = Date.parseDate(value, this.format);
22365          if (!v && this.useIso) {
22366             v = Date.parseDate(value, 'Y-m-d');
22367         }
22368         if(!v && this.altFormats){
22369             if(!this.altFormatsArray){
22370                 this.altFormatsArray = this.altFormats.split("|");
22371             }
22372             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22373                 v = Date.parseDate(value, this.altFormatsArray[i]);
22374             }
22375         }
22376         return v;
22377     },
22378
22379     // private
22380     formatDate : function(date, fmt){
22381         return (!date || !(date instanceof Date)) ?
22382                date : date.dateFormat(fmt || this.format);
22383     },
22384
22385     // private
22386     menuListeners : {
22387         select: function(m, d){
22388             
22389             this.setValue(d);
22390             this.fireEvent('select', this, d);
22391         },
22392         show : function(){ // retain focus styling
22393             this.onFocus();
22394         },
22395         hide : function(){
22396             this.focus.defer(10, this);
22397             var ml = this.menuListeners;
22398             this.menu.un("select", ml.select,  this);
22399             this.menu.un("show", ml.show,  this);
22400             this.menu.un("hide", ml.hide,  this);
22401         }
22402     },
22403
22404     // private
22405     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22406     onTriggerClick : function(){
22407         if(this.disabled){
22408             return;
22409         }
22410         if(this.menu == null){
22411             this.menu = new Roo.menu.DateMenu();
22412         }
22413         Roo.apply(this.menu.picker,  {
22414             showClear: this.allowBlank,
22415             minDate : this.minValue,
22416             maxDate : this.maxValue,
22417             disabledDatesRE : this.ddMatch,
22418             disabledDatesText : this.disabledDatesText,
22419             disabledDays : this.disabledDays,
22420             disabledDaysText : this.disabledDaysText,
22421             format : this.useIso ? 'Y-m-d' : this.format,
22422             minText : String.format(this.minText, this.formatDate(this.minValue)),
22423             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22424         });
22425         this.menu.on(Roo.apply({}, this.menuListeners, {
22426             scope:this
22427         }));
22428         this.menu.picker.setValue(this.getValue() || new Date());
22429         this.menu.show(this.el, "tl-bl?");
22430     },
22431
22432     beforeBlur : function(){
22433         var v = this.parseDate(this.getRawValue());
22434         if(v){
22435             this.setValue(v);
22436         }
22437     },
22438
22439     /*@
22440      * overide
22441      * 
22442      */
22443     isDirty : function() {
22444         if(this.disabled) {
22445             return false;
22446         }
22447         
22448         if(typeof(this.startValue) === 'undefined'){
22449             return false;
22450         }
22451         
22452         return String(this.getValue()) !== String(this.startValue);
22453         
22454     }
22455 });/*
22456  * Based on:
22457  * Ext JS Library 1.1.1
22458  * Copyright(c) 2006-2007, Ext JS, LLC.
22459  *
22460  * Originally Released Under LGPL - original licence link has changed is not relivant.
22461  *
22462  * Fork - LGPL
22463  * <script type="text/javascript">
22464  */
22465  
22466 /**
22467  * @class Roo.form.MonthField
22468  * @extends Roo.form.TriggerField
22469  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22470 * @constructor
22471 * Create a new MonthField
22472 * @param {Object} config
22473  */
22474 Roo.form.MonthField = function(config){
22475     
22476     Roo.form.MonthField.superclass.constructor.call(this, config);
22477     
22478       this.addEvents({
22479          
22480         /**
22481          * @event select
22482          * Fires when a date is selected
22483              * @param {Roo.form.MonthFieeld} combo This combo box
22484              * @param {Date} date The date selected
22485              */
22486         'select' : true
22487          
22488     });
22489     
22490     
22491     if(typeof this.minValue == "string") {
22492         this.minValue = this.parseDate(this.minValue);
22493     }
22494     if(typeof this.maxValue == "string") {
22495         this.maxValue = this.parseDate(this.maxValue);
22496     }
22497     this.ddMatch = null;
22498     if(this.disabledDates){
22499         var dd = this.disabledDates;
22500         var re = "(?:";
22501         for(var i = 0; i < dd.length; i++){
22502             re += dd[i];
22503             if(i != dd.length-1) {
22504                 re += "|";
22505             }
22506         }
22507         this.ddMatch = new RegExp(re + ")");
22508     }
22509 };
22510
22511 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
22512     /**
22513      * @cfg {String} format
22514      * The default date format string which can be overriden for localization support.  The format must be
22515      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22516      */
22517     format : "M Y",
22518     /**
22519      * @cfg {String} altFormats
22520      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22521      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22522      */
22523     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
22524     /**
22525      * @cfg {Array} disabledDays
22526      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22527      */
22528     disabledDays : [0,1,2,3,4,5,6],
22529     /**
22530      * @cfg {String} disabledDaysText
22531      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22532      */
22533     disabledDaysText : "Disabled",
22534     /**
22535      * @cfg {Array} disabledDates
22536      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22537      * expression so they are very powerful. Some examples:
22538      * <ul>
22539      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22540      * <li>["03/08", "09/16"] would disable those days for every year</li>
22541      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22542      * <li>["03/../2006"] would disable every day in March 2006</li>
22543      * <li>["^03"] would disable every day in every March</li>
22544      * </ul>
22545      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22546      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22547      */
22548     disabledDates : null,
22549     /**
22550      * @cfg {String} disabledDatesText
22551      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22552      */
22553     disabledDatesText : "Disabled",
22554     /**
22555      * @cfg {Date/String} minValue
22556      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22557      * valid format (defaults to null).
22558      */
22559     minValue : null,
22560     /**
22561      * @cfg {Date/String} maxValue
22562      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22563      * valid format (defaults to null).
22564      */
22565     maxValue : null,
22566     /**
22567      * @cfg {String} minText
22568      * The error text to display when the date in the cell is before minValue (defaults to
22569      * 'The date in this field must be after {minValue}').
22570      */
22571     minText : "The date in this field must be equal to or after {0}",
22572     /**
22573      * @cfg {String} maxTextf
22574      * The error text to display when the date in the cell is after maxValue (defaults to
22575      * 'The date in this field must be before {maxValue}').
22576      */
22577     maxText : "The date in this field must be equal to or before {0}",
22578     /**
22579      * @cfg {String} invalidText
22580      * The error text to display when the date in the field is invalid (defaults to
22581      * '{value} is not a valid date - it must be in the format {format}').
22582      */
22583     invalidText : "{0} is not a valid date - it must be in the format {1}",
22584     /**
22585      * @cfg {String} triggerClass
22586      * An additional CSS class used to style the trigger button.  The trigger will always get the
22587      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22588      * which displays a calendar icon).
22589      */
22590     triggerClass : 'x-form-date-trigger',
22591     
22592
22593     /**
22594      * @cfg {Boolean} useIso
22595      * if enabled, then the date field will use a hidden field to store the 
22596      * real value as iso formated date. default (true)
22597      */ 
22598     useIso : true,
22599     /**
22600      * @cfg {String/Object} autoCreate
22601      * A DomHelper element spec, or true for a default element spec (defaults to
22602      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22603      */ 
22604     // private
22605     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
22606     
22607     // private
22608     hiddenField: false,
22609     
22610     hideMonthPicker : false,
22611     
22612     onRender : function(ct, position)
22613     {
22614         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
22615         if (this.useIso) {
22616             this.el.dom.removeAttribute('name'); 
22617             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22618                     'before', true);
22619             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22620             // prevent input submission
22621             this.hiddenName = this.name;
22622         }
22623             
22624             
22625     },
22626     
22627     // private
22628     validateValue : function(value)
22629     {
22630         value = this.formatDate(value);
22631         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
22632             return false;
22633         }
22634         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22635              return true;
22636         }
22637         var svalue = value;
22638         value = this.parseDate(value);
22639         if(!value){
22640             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22641             return false;
22642         }
22643         var time = value.getTime();
22644         if(this.minValue && time < this.minValue.getTime()){
22645             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22646             return false;
22647         }
22648         if(this.maxValue && time > this.maxValue.getTime()){
22649             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22650             return false;
22651         }
22652         /*if(this.disabledDays){
22653             var day = value.getDay();
22654             for(var i = 0; i < this.disabledDays.length; i++) {
22655                 if(day === this.disabledDays[i]){
22656                     this.markInvalid(this.disabledDaysText);
22657                     return false;
22658                 }
22659             }
22660         }
22661         */
22662         var fvalue = this.formatDate(value);
22663         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
22664             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22665             return false;
22666         }
22667         */
22668         return true;
22669     },
22670
22671     // private
22672     // Provides logic to override the default TriggerField.validateBlur which just returns true
22673     validateBlur : function(){
22674         return !this.menu || !this.menu.isVisible();
22675     },
22676
22677     /**
22678      * Returns the current date value of the date field.
22679      * @return {Date} The date value
22680      */
22681     getValue : function(){
22682         
22683         
22684         
22685         return  this.hiddenField ?
22686                 this.hiddenField.value :
22687                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
22688     },
22689
22690     /**
22691      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22692      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
22693      * (the default format used is "m/d/y").
22694      * <br />Usage:
22695      * <pre><code>
22696 //All of these calls set the same date value (May 4, 2006)
22697
22698 //Pass a date object:
22699 var dt = new Date('5/4/06');
22700 monthField.setValue(dt);
22701
22702 //Pass a date string (default format):
22703 monthField.setValue('5/4/06');
22704
22705 //Pass a date string (custom format):
22706 monthField.format = 'Y-m-d';
22707 monthField.setValue('2006-5-4');
22708 </code></pre>
22709      * @param {String/Date} date The date or valid date string
22710      */
22711     setValue : function(date){
22712         Roo.log('month setValue' + date);
22713         // can only be first of month..
22714         
22715         var val = this.parseDate(date);
22716         
22717         if (this.hiddenField) {
22718             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22719         }
22720         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22721         this.value = this.parseDate(date);
22722     },
22723
22724     // private
22725     parseDate : function(value){
22726         if(!value || value instanceof Date){
22727             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
22728             return value;
22729         }
22730         var v = Date.parseDate(value, this.format);
22731         if (!v && this.useIso) {
22732             v = Date.parseDate(value, 'Y-m-d');
22733         }
22734         if (v) {
22735             // 
22736             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
22737         }
22738         
22739         
22740         if(!v && this.altFormats){
22741             if(!this.altFormatsArray){
22742                 this.altFormatsArray = this.altFormats.split("|");
22743             }
22744             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22745                 v = Date.parseDate(value, this.altFormatsArray[i]);
22746             }
22747         }
22748         return v;
22749     },
22750
22751     // private
22752     formatDate : function(date, fmt){
22753         return (!date || !(date instanceof Date)) ?
22754                date : date.dateFormat(fmt || this.format);
22755     },
22756
22757     // private
22758     menuListeners : {
22759         select: function(m, d){
22760             this.setValue(d);
22761             this.fireEvent('select', this, d);
22762         },
22763         show : function(){ // retain focus styling
22764             this.onFocus();
22765         },
22766         hide : function(){
22767             this.focus.defer(10, this);
22768             var ml = this.menuListeners;
22769             this.menu.un("select", ml.select,  this);
22770             this.menu.un("show", ml.show,  this);
22771             this.menu.un("hide", ml.hide,  this);
22772         }
22773     },
22774     // private
22775     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22776     onTriggerClick : function(){
22777         if(this.disabled){
22778             return;
22779         }
22780         if(this.menu == null){
22781             this.menu = new Roo.menu.DateMenu();
22782            
22783         }
22784         
22785         Roo.apply(this.menu.picker,  {
22786             
22787             showClear: this.allowBlank,
22788             minDate : this.minValue,
22789             maxDate : this.maxValue,
22790             disabledDatesRE : this.ddMatch,
22791             disabledDatesText : this.disabledDatesText,
22792             
22793             format : this.useIso ? 'Y-m-d' : this.format,
22794             minText : String.format(this.minText, this.formatDate(this.minValue)),
22795             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22796             
22797         });
22798          this.menu.on(Roo.apply({}, this.menuListeners, {
22799             scope:this
22800         }));
22801        
22802         
22803         var m = this.menu;
22804         var p = m.picker;
22805         
22806         // hide month picker get's called when we called by 'before hide';
22807         
22808         var ignorehide = true;
22809         p.hideMonthPicker  = function(disableAnim){
22810             if (ignorehide) {
22811                 return;
22812             }
22813              if(this.monthPicker){
22814                 Roo.log("hideMonthPicker called");
22815                 if(disableAnim === true){
22816                     this.monthPicker.hide();
22817                 }else{
22818                     this.monthPicker.slideOut('t', {duration:.2});
22819                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
22820                     p.fireEvent("select", this, this.value);
22821                     m.hide();
22822                 }
22823             }
22824         }
22825         
22826         Roo.log('picker set value');
22827         Roo.log(this.getValue());
22828         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
22829         m.show(this.el, 'tl-bl?');
22830         ignorehide  = false;
22831         // this will trigger hideMonthPicker..
22832         
22833         
22834         // hidden the day picker
22835         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
22836         
22837         
22838         
22839       
22840         
22841         p.showMonthPicker.defer(100, p);
22842     
22843         
22844        
22845     },
22846
22847     beforeBlur : function(){
22848         var v = this.parseDate(this.getRawValue());
22849         if(v){
22850             this.setValue(v);
22851         }
22852     }
22853
22854     /** @cfg {Boolean} grow @hide */
22855     /** @cfg {Number} growMin @hide */
22856     /** @cfg {Number} growMax @hide */
22857     /**
22858      * @hide
22859      * @method autoSize
22860      */
22861 });/*
22862  * Based on:
22863  * Ext JS Library 1.1.1
22864  * Copyright(c) 2006-2007, Ext JS, LLC.
22865  *
22866  * Originally Released Under LGPL - original licence link has changed is not relivant.
22867  *
22868  * Fork - LGPL
22869  * <script type="text/javascript">
22870  */
22871  
22872
22873 /**
22874  * @class Roo.form.ComboBox
22875  * @extends Roo.form.TriggerField
22876  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22877  * @constructor
22878  * Create a new ComboBox.
22879  * @param {Object} config Configuration options
22880  */
22881 Roo.form.ComboBox = function(config){
22882     Roo.form.ComboBox.superclass.constructor.call(this, config);
22883     this.addEvents({
22884         /**
22885          * @event expand
22886          * Fires when the dropdown list is expanded
22887              * @param {Roo.form.ComboBox} combo This combo box
22888              */
22889         'expand' : true,
22890         /**
22891          * @event collapse
22892          * Fires when the dropdown list is collapsed
22893              * @param {Roo.form.ComboBox} combo This combo box
22894              */
22895         'collapse' : true,
22896         /**
22897          * @event beforeselect
22898          * Fires before a list item is selected. Return false to cancel the selection.
22899              * @param {Roo.form.ComboBox} combo This combo box
22900              * @param {Roo.data.Record} record The data record returned from the underlying store
22901              * @param {Number} index The index of the selected item in the dropdown list
22902              */
22903         'beforeselect' : true,
22904         /**
22905          * @event select
22906          * Fires when a list item is selected
22907              * @param {Roo.form.ComboBox} combo This combo box
22908              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22909              * @param {Number} index The index of the selected item in the dropdown list
22910              */
22911         'select' : true,
22912         /**
22913          * @event beforequery
22914          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22915          * The event object passed has these properties:
22916              * @param {Roo.form.ComboBox} combo This combo box
22917              * @param {String} query The query
22918              * @param {Boolean} forceAll true to force "all" query
22919              * @param {Boolean} cancel true to cancel the query
22920              * @param {Object} e The query event object
22921              */
22922         'beforequery': true,
22923          /**
22924          * @event add
22925          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22926              * @param {Roo.form.ComboBox} combo This combo box
22927              */
22928         'add' : true,
22929         /**
22930          * @event edit
22931          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22932              * @param {Roo.form.ComboBox} combo This combo box
22933              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22934              */
22935         'edit' : true
22936         
22937         
22938     });
22939     if(this.transform){
22940         this.allowDomMove = false;
22941         var s = Roo.getDom(this.transform);
22942         if(!this.hiddenName){
22943             this.hiddenName = s.name;
22944         }
22945         if(!this.store){
22946             this.mode = 'local';
22947             var d = [], opts = s.options;
22948             for(var i = 0, len = opts.length;i < len; i++){
22949                 var o = opts[i];
22950                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22951                 if(o.selected) {
22952                     this.value = value;
22953                 }
22954                 d.push([value, o.text]);
22955             }
22956             this.store = new Roo.data.SimpleStore({
22957                 'id': 0,
22958                 fields: ['value', 'text'],
22959                 data : d
22960             });
22961             this.valueField = 'value';
22962             this.displayField = 'text';
22963         }
22964         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22965         if(!this.lazyRender){
22966             this.target = true;
22967             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22968             s.parentNode.removeChild(s); // remove it
22969             this.render(this.el.parentNode);
22970         }else{
22971             s.parentNode.removeChild(s); // remove it
22972         }
22973
22974     }
22975     if (this.store) {
22976         this.store = Roo.factory(this.store, Roo.data);
22977     }
22978     
22979     this.selectedIndex = -1;
22980     if(this.mode == 'local'){
22981         if(config.queryDelay === undefined){
22982             this.queryDelay = 10;
22983         }
22984         if(config.minChars === undefined){
22985             this.minChars = 0;
22986         }
22987     }
22988 };
22989
22990 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22991     /**
22992      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22993      */
22994     /**
22995      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22996      * rendering into an Roo.Editor, defaults to false)
22997      */
22998     /**
22999      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
23000      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
23001      */
23002     /**
23003      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
23004      */
23005     /**
23006      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
23007      * the dropdown list (defaults to undefined, with no header element)
23008      */
23009
23010      /**
23011      * @cfg {String/Roo.Template} tpl The template to use to render the output
23012      */
23013      
23014     // private
23015     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
23016     /**
23017      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
23018      */
23019     listWidth: undefined,
23020     /**
23021      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
23022      * mode = 'remote' or 'text' if mode = 'local')
23023      */
23024     displayField: undefined,
23025     /**
23026      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
23027      * mode = 'remote' or 'value' if mode = 'local'). 
23028      * Note: use of a valueField requires the user make a selection
23029      * in order for a value to be mapped.
23030      */
23031     valueField: undefined,
23032     
23033     
23034     /**
23035      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
23036      * field's data value (defaults to the underlying DOM element's name)
23037      */
23038     hiddenName: undefined,
23039     /**
23040      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
23041      */
23042     listClass: '',
23043     /**
23044      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
23045      */
23046     selectedClass: 'x-combo-selected',
23047     /**
23048      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
23049      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
23050      * which displays a downward arrow icon).
23051      */
23052     triggerClass : 'x-form-arrow-trigger',
23053     /**
23054      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
23055      */
23056     shadow:'sides',
23057     /**
23058      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
23059      * anchor positions (defaults to 'tl-bl')
23060      */
23061     listAlign: 'tl-bl?',
23062     /**
23063      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
23064      */
23065     maxHeight: 300,
23066     /**
23067      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
23068      * query specified by the allQuery config option (defaults to 'query')
23069      */
23070     triggerAction: 'query',
23071     /**
23072      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
23073      * (defaults to 4, does not apply if editable = false)
23074      */
23075     minChars : 4,
23076     /**
23077      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
23078      * delay (typeAheadDelay) if it matches a known value (defaults to false)
23079      */
23080     typeAhead: false,
23081     /**
23082      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23083      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23084      */
23085     queryDelay: 500,
23086     /**
23087      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23088      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
23089      */
23090     pageSize: 0,
23091     /**
23092      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23093      * when editable = true (defaults to false)
23094      */
23095     selectOnFocus:false,
23096     /**
23097      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23098      */
23099     queryParam: 'query',
23100     /**
23101      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23102      * when mode = 'remote' (defaults to 'Loading...')
23103      */
23104     loadingText: 'Loading...',
23105     /**
23106      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23107      */
23108     resizable: false,
23109     /**
23110      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23111      */
23112     handleHeight : 8,
23113     /**
23114      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23115      * traditional select (defaults to true)
23116      */
23117     editable: true,
23118     /**
23119      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23120      */
23121     allQuery: '',
23122     /**
23123      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23124      */
23125     mode: 'remote',
23126     /**
23127      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23128      * listWidth has a higher value)
23129      */
23130     minListWidth : 70,
23131     /**
23132      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23133      * allow the user to set arbitrary text into the field (defaults to false)
23134      */
23135     forceSelection:false,
23136     /**
23137      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23138      * if typeAhead = true (defaults to 250)
23139      */
23140     typeAheadDelay : 250,
23141     /**
23142      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23143      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23144      */
23145     valueNotFoundText : undefined,
23146     /**
23147      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23148      */
23149     blockFocus : false,
23150     
23151     /**
23152      * @cfg {Boolean} disableClear Disable showing of clear button.
23153      */
23154     disableClear : false,
23155     /**
23156      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23157      */
23158     alwaysQuery : false,
23159     
23160     //private
23161     addicon : false,
23162     editicon: false,
23163     
23164     // element that contains real text value.. (when hidden is used..)
23165      
23166     // private
23167     onRender : function(ct, position){
23168         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23169         if(this.hiddenName){
23170             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23171                     'before', true);
23172             this.hiddenField.value =
23173                 this.hiddenValue !== undefined ? this.hiddenValue :
23174                 this.value !== undefined ? this.value : '';
23175
23176             // prevent input submission
23177             this.el.dom.removeAttribute('name');
23178              
23179              
23180         }
23181         if(Roo.isGecko){
23182             this.el.dom.setAttribute('autocomplete', 'off');
23183         }
23184
23185         var cls = 'x-combo-list';
23186
23187         this.list = new Roo.Layer({
23188             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23189         });
23190
23191         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23192         this.list.setWidth(lw);
23193         this.list.swallowEvent('mousewheel');
23194         this.assetHeight = 0;
23195
23196         if(this.title){
23197             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23198             this.assetHeight += this.header.getHeight();
23199         }
23200
23201         this.innerList = this.list.createChild({cls:cls+'-inner'});
23202         this.innerList.on('mouseover', this.onViewOver, this);
23203         this.innerList.on('mousemove', this.onViewMove, this);
23204         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23205         
23206         if(this.allowBlank && !this.pageSize && !this.disableClear){
23207             this.footer = this.list.createChild({cls:cls+'-ft'});
23208             this.pageTb = new Roo.Toolbar(this.footer);
23209            
23210         }
23211         if(this.pageSize){
23212             this.footer = this.list.createChild({cls:cls+'-ft'});
23213             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23214                     {pageSize: this.pageSize});
23215             
23216         }
23217         
23218         if (this.pageTb && this.allowBlank && !this.disableClear) {
23219             var _this = this;
23220             this.pageTb.add(new Roo.Toolbar.Fill(), {
23221                 cls: 'x-btn-icon x-btn-clear',
23222                 text: '&#160;',
23223                 handler: function()
23224                 {
23225                     _this.collapse();
23226                     _this.clearValue();
23227                     _this.onSelect(false, -1);
23228                 }
23229             });
23230         }
23231         if (this.footer) {
23232             this.assetHeight += this.footer.getHeight();
23233         }
23234         
23235
23236         if(!this.tpl){
23237             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23238         }
23239
23240         this.view = new Roo.View(this.innerList, this.tpl, {
23241             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23242         });
23243
23244         this.view.on('click', this.onViewClick, this);
23245
23246         this.store.on('beforeload', this.onBeforeLoad, this);
23247         this.store.on('load', this.onLoad, this);
23248         this.store.on('loadexception', this.onLoadException, this);
23249
23250         if(this.resizable){
23251             this.resizer = new Roo.Resizable(this.list,  {
23252                pinned:true, handles:'se'
23253             });
23254             this.resizer.on('resize', function(r, w, h){
23255                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23256                 this.listWidth = w;
23257                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23258                 this.restrictHeight();
23259             }, this);
23260             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23261         }
23262         if(!this.editable){
23263             this.editable = true;
23264             this.setEditable(false);
23265         }  
23266         
23267         
23268         if (typeof(this.events.add.listeners) != 'undefined') {
23269             
23270             this.addicon = this.wrap.createChild(
23271                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23272        
23273             this.addicon.on('click', function(e) {
23274                 this.fireEvent('add', this);
23275             }, this);
23276         }
23277         if (typeof(this.events.edit.listeners) != 'undefined') {
23278             
23279             this.editicon = this.wrap.createChild(
23280                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23281             if (this.addicon) {
23282                 this.editicon.setStyle('margin-left', '40px');
23283             }
23284             this.editicon.on('click', function(e) {
23285                 
23286                 // we fire even  if inothing is selected..
23287                 this.fireEvent('edit', this, this.lastData );
23288                 
23289             }, this);
23290         }
23291         
23292         
23293         
23294     },
23295
23296     // private
23297     initEvents : function(){
23298         Roo.form.ComboBox.superclass.initEvents.call(this);
23299
23300         this.keyNav = new Roo.KeyNav(this.el, {
23301             "up" : function(e){
23302                 this.inKeyMode = true;
23303                 this.selectPrev();
23304             },
23305
23306             "down" : function(e){
23307                 if(!this.isExpanded()){
23308                     this.onTriggerClick();
23309                 }else{
23310                     this.inKeyMode = true;
23311                     this.selectNext();
23312                 }
23313             },
23314
23315             "enter" : function(e){
23316                 this.onViewClick();
23317                 //return true;
23318             },
23319
23320             "esc" : function(e){
23321                 this.collapse();
23322             },
23323
23324             "tab" : function(e){
23325                 this.onViewClick(false);
23326                 this.fireEvent("specialkey", this, e);
23327                 return true;
23328             },
23329
23330             scope : this,
23331
23332             doRelay : function(foo, bar, hname){
23333                 if(hname == 'down' || this.scope.isExpanded()){
23334                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23335                 }
23336                 return true;
23337             },
23338
23339             forceKeyDown: true
23340         });
23341         this.queryDelay = Math.max(this.queryDelay || 10,
23342                 this.mode == 'local' ? 10 : 250);
23343         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23344         if(this.typeAhead){
23345             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23346         }
23347         if(this.editable !== false){
23348             this.el.on("keyup", this.onKeyUp, this);
23349         }
23350         if(this.forceSelection){
23351             this.on('blur', this.doForce, this);
23352         }
23353     },
23354
23355     onDestroy : function(){
23356         if(this.view){
23357             this.view.setStore(null);
23358             this.view.el.removeAllListeners();
23359             this.view.el.remove();
23360             this.view.purgeListeners();
23361         }
23362         if(this.list){
23363             this.list.destroy();
23364         }
23365         if(this.store){
23366             this.store.un('beforeload', this.onBeforeLoad, this);
23367             this.store.un('load', this.onLoad, this);
23368             this.store.un('loadexception', this.onLoadException, this);
23369         }
23370         Roo.form.ComboBox.superclass.onDestroy.call(this);
23371     },
23372
23373     // private
23374     fireKey : function(e){
23375         if(e.isNavKeyPress() && !this.list.isVisible()){
23376             this.fireEvent("specialkey", this, e);
23377         }
23378     },
23379
23380     // private
23381     onResize: function(w, h){
23382         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23383         
23384         if(typeof w != 'number'){
23385             // we do not handle it!?!?
23386             return;
23387         }
23388         var tw = this.trigger.getWidth();
23389         tw += this.addicon ? this.addicon.getWidth() : 0;
23390         tw += this.editicon ? this.editicon.getWidth() : 0;
23391         var x = w - tw;
23392         this.el.setWidth( this.adjustWidth('input', x));
23393             
23394         this.trigger.setStyle('left', x+'px');
23395         
23396         if(this.list && this.listWidth === undefined){
23397             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23398             this.list.setWidth(lw);
23399             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23400         }
23401         
23402     
23403         
23404     },
23405
23406     /**
23407      * Allow or prevent the user from directly editing the field text.  If false is passed,
23408      * the user will only be able to select from the items defined in the dropdown list.  This method
23409      * is the runtime equivalent of setting the 'editable' config option at config time.
23410      * @param {Boolean} value True to allow the user to directly edit the field text
23411      */
23412     setEditable : function(value){
23413         if(value == this.editable){
23414             return;
23415         }
23416         this.editable = value;
23417         if(!value){
23418             this.el.dom.setAttribute('readOnly', true);
23419             this.el.on('mousedown', this.onTriggerClick,  this);
23420             this.el.addClass('x-combo-noedit');
23421         }else{
23422             this.el.dom.setAttribute('readOnly', false);
23423             this.el.un('mousedown', this.onTriggerClick,  this);
23424             this.el.removeClass('x-combo-noedit');
23425         }
23426     },
23427
23428     // private
23429     onBeforeLoad : function(){
23430         if(!this.hasFocus){
23431             return;
23432         }
23433         this.innerList.update(this.loadingText ?
23434                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23435         this.restrictHeight();
23436         this.selectedIndex = -1;
23437     },
23438
23439     // private
23440     onLoad : function(){
23441         if(!this.hasFocus){
23442             return;
23443         }
23444         if(this.store.getCount() > 0){
23445             this.expand();
23446             this.restrictHeight();
23447             if(this.lastQuery == this.allQuery){
23448                 if(this.editable){
23449                     this.el.dom.select();
23450                 }
23451                 if(!this.selectByValue(this.value, true)){
23452                     this.select(0, true);
23453                 }
23454             }else{
23455                 this.selectNext();
23456                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23457                     this.taTask.delay(this.typeAheadDelay);
23458                 }
23459             }
23460         }else{
23461             this.onEmptyResults();
23462         }
23463         //this.el.focus();
23464     },
23465     // private
23466     onLoadException : function()
23467     {
23468         this.collapse();
23469         Roo.log(this.store.reader.jsonData);
23470         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
23471             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
23472         }
23473         
23474         
23475     },
23476     // private
23477     onTypeAhead : function(){
23478         if(this.store.getCount() > 0){
23479             var r = this.store.getAt(0);
23480             var newValue = r.data[this.displayField];
23481             var len = newValue.length;
23482             var selStart = this.getRawValue().length;
23483             if(selStart != len){
23484                 this.setRawValue(newValue);
23485                 this.selectText(selStart, newValue.length);
23486             }
23487         }
23488     },
23489
23490     // private
23491     onSelect : function(record, index){
23492         if(this.fireEvent('beforeselect', this, record, index) !== false){
23493             this.setFromData(index > -1 ? record.data : false);
23494             this.collapse();
23495             this.fireEvent('select', this, record, index);
23496         }
23497     },
23498
23499     /**
23500      * Returns the currently selected field value or empty string if no value is set.
23501      * @return {String} value The selected value
23502      */
23503     getValue : function(){
23504         if(this.valueField){
23505             return typeof this.value != 'undefined' ? this.value : '';
23506         }
23507         return Roo.form.ComboBox.superclass.getValue.call(this);
23508     },
23509
23510     /**
23511      * Clears any text/value currently set in the field
23512      */
23513     clearValue : function(){
23514         if(this.hiddenField){
23515             this.hiddenField.value = '';
23516         }
23517         this.value = '';
23518         this.setRawValue('');
23519         this.lastSelectionText = '';
23520         
23521     },
23522
23523     /**
23524      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23525      * will be displayed in the field.  If the value does not match the data value of an existing item,
23526      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23527      * Otherwise the field will be blank (although the value will still be set).
23528      * @param {String} value The value to match
23529      */
23530     setValue : function(v){
23531         var text = v;
23532         if(this.valueField){
23533             var r = this.findRecord(this.valueField, v);
23534             if(r){
23535                 text = r.data[this.displayField];
23536             }else if(this.valueNotFoundText !== undefined){
23537                 text = this.valueNotFoundText;
23538             }
23539         }
23540         this.lastSelectionText = text;
23541         if(this.hiddenField){
23542             this.hiddenField.value = v;
23543         }
23544         Roo.form.ComboBox.superclass.setValue.call(this, text);
23545         this.value = v;
23546     },
23547     /**
23548      * @property {Object} the last set data for the element
23549      */
23550     
23551     lastData : false,
23552     /**
23553      * Sets the value of the field based on a object which is related to the record format for the store.
23554      * @param {Object} value the value to set as. or false on reset?
23555      */
23556     setFromData : function(o){
23557         var dv = ''; // display value
23558         var vv = ''; // value value..
23559         this.lastData = o;
23560         if (this.displayField) {
23561             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23562         } else {
23563             // this is an error condition!!!
23564             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23565         }
23566         
23567         if(this.valueField){
23568             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23569         }
23570         if(this.hiddenField){
23571             this.hiddenField.value = vv;
23572             
23573             this.lastSelectionText = dv;
23574             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23575             this.value = vv;
23576             return;
23577         }
23578         // no hidden field.. - we store the value in 'value', but still display
23579         // display field!!!!
23580         this.lastSelectionText = dv;
23581         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23582         this.value = vv;
23583         
23584         
23585     },
23586     // private
23587     reset : function(){
23588         // overridden so that last data is reset..
23589         this.setValue(this.resetValue);
23590         this.clearInvalid();
23591         this.lastData = false;
23592         if (this.view) {
23593             this.view.clearSelections();
23594         }
23595     },
23596     // private
23597     findRecord : function(prop, value){
23598         var record;
23599         if(this.store.getCount() > 0){
23600             this.store.each(function(r){
23601                 if(r.data[prop] == value){
23602                     record = r;
23603                     return false;
23604                 }
23605                 return true;
23606             });
23607         }
23608         return record;
23609     },
23610     
23611     getName: function()
23612     {
23613         // returns hidden if it's set..
23614         if (!this.rendered) {return ''};
23615         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
23616         
23617     },
23618     // private
23619     onViewMove : function(e, t){
23620         this.inKeyMode = false;
23621     },
23622
23623     // private
23624     onViewOver : function(e, t){
23625         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23626             return;
23627         }
23628         var item = this.view.findItemFromChild(t);
23629         if(item){
23630             var index = this.view.indexOf(item);
23631             this.select(index, false);
23632         }
23633     },
23634
23635     // private
23636     onViewClick : function(doFocus)
23637     {
23638         var index = this.view.getSelectedIndexes()[0];
23639         var r = this.store.getAt(index);
23640         if(r){
23641             this.onSelect(r, index);
23642         }
23643         if(doFocus !== false && !this.blockFocus){
23644             this.el.focus();
23645         }
23646     },
23647
23648     // private
23649     restrictHeight : function(){
23650         this.innerList.dom.style.height = '';
23651         var inner = this.innerList.dom;
23652         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23653         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23654         this.list.beginUpdate();
23655         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23656         this.list.alignTo(this.el, this.listAlign);
23657         this.list.endUpdate();
23658     },
23659
23660     // private
23661     onEmptyResults : function(){
23662         this.collapse();
23663     },
23664
23665     /**
23666      * Returns true if the dropdown list is expanded, else false.
23667      */
23668     isExpanded : function(){
23669         return this.list.isVisible();
23670     },
23671
23672     /**
23673      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23674      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23675      * @param {String} value The data value of the item to select
23676      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23677      * selected item if it is not currently in view (defaults to true)
23678      * @return {Boolean} True if the value matched an item in the list, else false
23679      */
23680     selectByValue : function(v, scrollIntoView){
23681         if(v !== undefined && v !== null){
23682             var r = this.findRecord(this.valueField || this.displayField, v);
23683             if(r){
23684                 this.select(this.store.indexOf(r), scrollIntoView);
23685                 return true;
23686             }
23687         }
23688         return false;
23689     },
23690
23691     /**
23692      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23693      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23694      * @param {Number} index The zero-based index of the list item to select
23695      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23696      * selected item if it is not currently in view (defaults to true)
23697      */
23698     select : function(index, scrollIntoView){
23699         this.selectedIndex = index;
23700         this.view.select(index);
23701         if(scrollIntoView !== false){
23702             var el = this.view.getNode(index);
23703             if(el){
23704                 this.innerList.scrollChildIntoView(el, false);
23705             }
23706         }
23707     },
23708
23709     // private
23710     selectNext : function(){
23711         var ct = this.store.getCount();
23712         if(ct > 0){
23713             if(this.selectedIndex == -1){
23714                 this.select(0);
23715             }else if(this.selectedIndex < ct-1){
23716                 this.select(this.selectedIndex+1);
23717             }
23718         }
23719     },
23720
23721     // private
23722     selectPrev : function(){
23723         var ct = this.store.getCount();
23724         if(ct > 0){
23725             if(this.selectedIndex == -1){
23726                 this.select(0);
23727             }else if(this.selectedIndex != 0){
23728                 this.select(this.selectedIndex-1);
23729             }
23730         }
23731     },
23732
23733     // private
23734     onKeyUp : function(e){
23735         if(this.editable !== false && !e.isSpecialKey()){
23736             this.lastKey = e.getKey();
23737             this.dqTask.delay(this.queryDelay);
23738         }
23739     },
23740
23741     // private
23742     validateBlur : function(){
23743         return !this.list || !this.list.isVisible();   
23744     },
23745
23746     // private
23747     initQuery : function(){
23748         this.doQuery(this.getRawValue());
23749     },
23750
23751     // private
23752     doForce : function(){
23753         if(this.el.dom.value.length > 0){
23754             this.el.dom.value =
23755                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23756              
23757         }
23758     },
23759
23760     /**
23761      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23762      * query allowing the query action to be canceled if needed.
23763      * @param {String} query The SQL query to execute
23764      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23765      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23766      * saved in the current store (defaults to false)
23767      */
23768     doQuery : function(q, forceAll){
23769         if(q === undefined || q === null){
23770             q = '';
23771         }
23772         var qe = {
23773             query: q,
23774             forceAll: forceAll,
23775             combo: this,
23776             cancel:false
23777         };
23778         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23779             return false;
23780         }
23781         q = qe.query;
23782         forceAll = qe.forceAll;
23783         if(forceAll === true || (q.length >= this.minChars)){
23784             if(this.lastQuery != q || this.alwaysQuery){
23785                 this.lastQuery = q;
23786                 if(this.mode == 'local'){
23787                     this.selectedIndex = -1;
23788                     if(forceAll){
23789                         this.store.clearFilter();
23790                     }else{
23791                         this.store.filter(this.displayField, q);
23792                     }
23793                     this.onLoad();
23794                 }else{
23795                     this.store.baseParams[this.queryParam] = q;
23796                     this.store.load({
23797                         params: this.getParams(q)
23798                     });
23799                     this.expand();
23800                 }
23801             }else{
23802                 this.selectedIndex = -1;
23803                 this.onLoad();   
23804             }
23805         }
23806     },
23807
23808     // private
23809     getParams : function(q){
23810         var p = {};
23811         //p[this.queryParam] = q;
23812         if(this.pageSize){
23813             p.start = 0;
23814             p.limit = this.pageSize;
23815         }
23816         return p;
23817     },
23818
23819     /**
23820      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23821      */
23822     collapse : function(){
23823         if(!this.isExpanded()){
23824             return;
23825         }
23826         this.list.hide();
23827         Roo.get(document).un('mousedown', this.collapseIf, this);
23828         Roo.get(document).un('mousewheel', this.collapseIf, this);
23829         if (!this.editable) {
23830             Roo.get(document).un('keydown', this.listKeyPress, this);
23831         }
23832         this.fireEvent('collapse', this);
23833     },
23834
23835     // private
23836     collapseIf : function(e){
23837         if(!e.within(this.wrap) && !e.within(this.list)){
23838             this.collapse();
23839         }
23840     },
23841
23842     /**
23843      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23844      */
23845     expand : function(){
23846         if(this.isExpanded() || !this.hasFocus){
23847             return;
23848         }
23849         this.list.alignTo(this.el, this.listAlign);
23850         this.list.show();
23851         Roo.get(document).on('mousedown', this.collapseIf, this);
23852         Roo.get(document).on('mousewheel', this.collapseIf, this);
23853         if (!this.editable) {
23854             Roo.get(document).on('keydown', this.listKeyPress, this);
23855         }
23856         
23857         this.fireEvent('expand', this);
23858     },
23859
23860     // private
23861     // Implements the default empty TriggerField.onTriggerClick function
23862     onTriggerClick : function(){
23863         if(this.disabled){
23864             return;
23865         }
23866         if(this.isExpanded()){
23867             this.collapse();
23868             if (!this.blockFocus) {
23869                 this.el.focus();
23870             }
23871             
23872         }else {
23873             this.hasFocus = true;
23874             if(this.triggerAction == 'all') {
23875                 this.doQuery(this.allQuery, true);
23876             } else {
23877                 this.doQuery(this.getRawValue());
23878             }
23879             if (!this.blockFocus) {
23880                 this.el.focus();
23881             }
23882         }
23883     },
23884     listKeyPress : function(e)
23885     {
23886         //Roo.log('listkeypress');
23887         // scroll to first matching element based on key pres..
23888         if (e.isSpecialKey()) {
23889             return false;
23890         }
23891         var k = String.fromCharCode(e.getKey()).toUpperCase();
23892         //Roo.log(k);
23893         var match  = false;
23894         var csel = this.view.getSelectedNodes();
23895         var cselitem = false;
23896         if (csel.length) {
23897             var ix = this.view.indexOf(csel[0]);
23898             cselitem  = this.store.getAt(ix);
23899             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23900                 cselitem = false;
23901             }
23902             
23903         }
23904         
23905         this.store.each(function(v) { 
23906             if (cselitem) {
23907                 // start at existing selection.
23908                 if (cselitem.id == v.id) {
23909                     cselitem = false;
23910                 }
23911                 return;
23912             }
23913                 
23914             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23915                 match = this.store.indexOf(v);
23916                 return false;
23917             }
23918         }, this);
23919         
23920         if (match === false) {
23921             return true; // no more action?
23922         }
23923         // scroll to?
23924         this.view.select(match);
23925         var sn = Roo.get(this.view.getSelectedNodes()[0]);
23926         sn.scrollIntoView(sn.dom.parentNode, false);
23927     }
23928
23929     /** 
23930     * @cfg {Boolean} grow 
23931     * @hide 
23932     */
23933     /** 
23934     * @cfg {Number} growMin 
23935     * @hide 
23936     */
23937     /** 
23938     * @cfg {Number} growMax 
23939     * @hide 
23940     */
23941     /**
23942      * @hide
23943      * @method autoSize
23944      */
23945 });/*
23946  * Copyright(c) 2010-2012, Roo J Solutions Limited
23947  *
23948  * Licence LGPL
23949  *
23950  */
23951
23952 /**
23953  * @class Roo.form.ComboBoxArray
23954  * @extends Roo.form.TextField
23955  * A facebook style adder... for lists of email / people / countries  etc...
23956  * pick multiple items from a combo box, and shows each one.
23957  *
23958  *  Fred [x]  Brian [x]  [Pick another |v]
23959  *
23960  *
23961  *  For this to work: it needs various extra information
23962  *    - normal combo problay has
23963  *      name, hiddenName
23964  *    + displayField, valueField
23965  *
23966  *    For our purpose...
23967  *
23968  *
23969  *   If we change from 'extends' to wrapping...
23970  *   
23971  *  
23972  *
23973  
23974  
23975  * @constructor
23976  * Create a new ComboBoxArray.
23977  * @param {Object} config Configuration options
23978  */
23979  
23980
23981 Roo.form.ComboBoxArray = function(config)
23982 {
23983     this.addEvents({
23984         /**
23985          * @event beforeremove
23986          * Fires before remove the value from the list
23987              * @param {Roo.form.ComboBoxArray} _self This combo box array
23988              * @param {Roo.form.ComboBoxArray.Item} item removed item
23989              */
23990         'beforeremove' : true,
23991         /**
23992          * @event remove
23993          * Fires when remove the value from the list
23994              * @param {Roo.form.ComboBoxArray} _self This combo box array
23995              * @param {Roo.form.ComboBoxArray.Item} item removed item
23996              */
23997         'remove' : true
23998         
23999         
24000     });
24001     
24002     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
24003     
24004     this.items = new Roo.util.MixedCollection(false);
24005     
24006     // construct the child combo...
24007     
24008     
24009     
24010     
24011    
24012     
24013 }
24014
24015  
24016 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
24017
24018     /**
24019      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
24020      */
24021     
24022     lastData : false,
24023     
24024     // behavies liek a hiddne field
24025     inputType:      'hidden',
24026     /**
24027      * @cfg {Number} width The width of the box that displays the selected element
24028      */ 
24029     width:          300,
24030
24031     
24032     
24033     /**
24034      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
24035      */
24036     name : false,
24037     /**
24038      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
24039      */
24040     hiddenName : false,
24041     
24042     
24043     // private the array of items that are displayed..
24044     items  : false,
24045     // private - the hidden field el.
24046     hiddenEl : false,
24047     // private - the filed el..
24048     el : false,
24049     
24050     //validateValue : function() { return true; }, // all values are ok!
24051     //onAddClick: function() { },
24052     
24053     onRender : function(ct, position) 
24054     {
24055         
24056         // create the standard hidden element
24057         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
24058         
24059         
24060         // give fake names to child combo;
24061         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
24062         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
24063         
24064         this.combo = Roo.factory(this.combo, Roo.form);
24065         this.combo.onRender(ct, position);
24066         if (typeof(this.combo.width) != 'undefined') {
24067             this.combo.onResize(this.combo.width,0);
24068         }
24069         
24070         this.combo.initEvents();
24071         
24072         // assigned so form know we need to do this..
24073         this.store          = this.combo.store;
24074         this.valueField     = this.combo.valueField;
24075         this.displayField   = this.combo.displayField ;
24076         
24077         
24078         this.combo.wrap.addClass('x-cbarray-grp');
24079         
24080         var cbwrap = this.combo.wrap.createChild(
24081             {tag: 'div', cls: 'x-cbarray-cb'},
24082             this.combo.el.dom
24083         );
24084         
24085              
24086         this.hiddenEl = this.combo.wrap.createChild({
24087             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
24088         });
24089         this.el = this.combo.wrap.createChild({
24090             tag: 'input',  type:'hidden' , name: this.name, value : ''
24091         });
24092          //   this.el.dom.removeAttribute("name");
24093         
24094         
24095         this.outerWrap = this.combo.wrap;
24096         this.wrap = cbwrap;
24097         
24098         this.outerWrap.setWidth(this.width);
24099         this.outerWrap.dom.removeChild(this.el.dom);
24100         
24101         this.wrap.dom.appendChild(this.el.dom);
24102         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
24103         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
24104         
24105         this.combo.trigger.setStyle('position','relative');
24106         this.combo.trigger.setStyle('left', '0px');
24107         this.combo.trigger.setStyle('top', '2px');
24108         
24109         this.combo.el.setStyle('vertical-align', 'text-bottom');
24110         
24111         //this.trigger.setStyle('vertical-align', 'top');
24112         
24113         // this should use the code from combo really... on('add' ....)
24114         if (this.adder) {
24115             
24116         
24117             this.adder = this.outerWrap.createChild(
24118                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
24119             var _t = this;
24120             this.adder.on('click', function(e) {
24121                 _t.fireEvent('adderclick', this, e);
24122             }, _t);
24123         }
24124         //var _t = this;
24125         //this.adder.on('click', this.onAddClick, _t);
24126         
24127         
24128         this.combo.on('select', function(cb, rec, ix) {
24129             this.addItem(rec.data);
24130             
24131             cb.setValue('');
24132             cb.el.dom.value = '';
24133             //cb.lastData = rec.data;
24134             // add to list
24135             
24136         }, this);
24137         
24138         
24139     },
24140     
24141     
24142     getName: function()
24143     {
24144         // returns hidden if it's set..
24145         if (!this.rendered) {return ''};
24146         return  this.hiddenName ? this.hiddenName : this.name;
24147         
24148     },
24149     
24150     
24151     onResize: function(w, h){
24152         
24153         return;
24154         // not sure if this is needed..
24155         //this.combo.onResize(w,h);
24156         
24157         if(typeof w != 'number'){
24158             // we do not handle it!?!?
24159             return;
24160         }
24161         var tw = this.combo.trigger.getWidth();
24162         tw += this.addicon ? this.addicon.getWidth() : 0;
24163         tw += this.editicon ? this.editicon.getWidth() : 0;
24164         var x = w - tw;
24165         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24166             
24167         this.combo.trigger.setStyle('left', '0px');
24168         
24169         if(this.list && this.listWidth === undefined){
24170             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24171             this.list.setWidth(lw);
24172             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24173         }
24174         
24175     
24176         
24177     },
24178     
24179     addItem: function(rec)
24180     {
24181         var valueField = this.combo.valueField;
24182         var displayField = this.combo.displayField;
24183         if (this.items.indexOfKey(rec[valueField]) > -1) {
24184             //console.log("GOT " + rec.data.id);
24185             return;
24186         }
24187         
24188         var x = new Roo.form.ComboBoxArray.Item({
24189             //id : rec[this.idField],
24190             data : rec,
24191             displayField : displayField ,
24192             tipField : displayField ,
24193             cb : this
24194         });
24195         // use the 
24196         this.items.add(rec[valueField],x);
24197         // add it before the element..
24198         this.updateHiddenEl();
24199         x.render(this.outerWrap, this.wrap.dom);
24200         // add the image handler..
24201     },
24202     
24203     updateHiddenEl : function()
24204     {
24205         this.validate();
24206         if (!this.hiddenEl) {
24207             return;
24208         }
24209         var ar = [];
24210         var idField = this.combo.valueField;
24211         
24212         this.items.each(function(f) {
24213             ar.push(f.data[idField]);
24214            
24215         });
24216         this.hiddenEl.dom.value = ar.join(',');
24217         this.validate();
24218     },
24219     
24220     reset : function()
24221     {
24222         this.items.clear();
24223         
24224         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
24225            el.remove();
24226         });
24227         
24228         this.el.dom.value = '';
24229         if (this.hiddenEl) {
24230             this.hiddenEl.dom.value = '';
24231         }
24232         
24233     },
24234     getValue: function()
24235     {
24236         return this.hiddenEl ? this.hiddenEl.dom.value : '';
24237     },
24238     setValue: function(v) // not a valid action - must use addItems..
24239     {
24240          
24241         this.reset();
24242         
24243         
24244         
24245         if (this.store.isLocal && (typeof(v) == 'string')) {
24246             // then we can use the store to find the values..
24247             // comma seperated at present.. this needs to allow JSON based encoding..
24248             this.hiddenEl.value  = v;
24249             var v_ar = [];
24250             Roo.each(v.split(','), function(k) {
24251                 Roo.log("CHECK " + this.valueField + ',' + k);
24252                 var li = this.store.query(this.valueField, k);
24253                 if (!li.length) {
24254                     return;
24255                 }
24256                 var add = {};
24257                 add[this.valueField] = k;
24258                 add[this.displayField] = li.item(0).data[this.displayField];
24259                 
24260                 this.addItem(add);
24261             }, this) 
24262              
24263         }
24264         if (typeof(v) == 'object' ) {
24265             // then let's assume it's an array of objects..
24266             Roo.each(v, function(l) {
24267                 this.addItem(l);
24268             }, this);
24269              
24270         }
24271         
24272         
24273     },
24274     setFromData: function(v)
24275     {
24276         // this recieves an object, if setValues is called.
24277         this.reset();
24278         this.el.dom.value = v[this.displayField];
24279         this.hiddenEl.dom.value = v[this.valueField];
24280         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24281             return;
24282         }
24283         var kv = v[this.valueField];
24284         var dv = v[this.displayField];
24285         kv = typeof(kv) != 'string' ? '' : kv;
24286         dv = typeof(dv) != 'string' ? '' : dv;
24287         
24288         
24289         var keys = kv.split(',');
24290         var display = dv.split(',');
24291         for (var i = 0 ; i < keys.length; i++) {
24292             
24293             add = {};
24294             add[this.valueField] = keys[i];
24295             add[this.displayField] = display[i];
24296             this.addItem(add);
24297         }
24298       
24299         
24300     },
24301     
24302     /**
24303      * Validates the combox array value
24304      * @return {Boolean} True if the value is valid, else false
24305      */
24306     validate : function(){
24307         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
24308             this.clearInvalid();
24309             return true;
24310         }
24311         return false;
24312     },
24313     
24314     validateValue : function(value){
24315         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24316         
24317     },
24318     
24319     /*@
24320      * overide
24321      * 
24322      */
24323     isDirty : function() {
24324         if(this.disabled) {
24325             return false;
24326         }
24327         
24328         try {
24329             var d = Roo.decode(String(this.originalValue));
24330         } catch (e) {
24331             return String(this.getValue()) !== String(this.originalValue);
24332         }
24333         
24334         var originalValue = [];
24335         
24336         for (var i = 0; i < d.length; i++){
24337             originalValue.push(d[i][this.valueField]);
24338         }
24339         
24340         return String(this.getValue()) !== String(originalValue.join(','));
24341         
24342     }
24343     
24344 });
24345
24346
24347
24348 /**
24349  * @class Roo.form.ComboBoxArray.Item
24350  * @extends Roo.BoxComponent
24351  * A selected item in the list
24352  *  Fred [x]  Brian [x]  [Pick another |v]
24353  * 
24354  * @constructor
24355  * Create a new item.
24356  * @param {Object} config Configuration options
24357  */
24358  
24359 Roo.form.ComboBoxArray.Item = function(config) {
24360     config.id = Roo.id();
24361     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24362 }
24363
24364 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24365     data : {},
24366     cb: false,
24367     displayField : false,
24368     tipField : false,
24369     
24370     
24371     defaultAutoCreate : {
24372         tag: 'div',
24373         cls: 'x-cbarray-item',
24374         cn : [ 
24375             { tag: 'div' },
24376             {
24377                 tag: 'img',
24378                 width:16,
24379                 height : 16,
24380                 src : Roo.BLANK_IMAGE_URL ,
24381                 align: 'center'
24382             }
24383         ]
24384         
24385     },
24386     
24387  
24388     onRender : function(ct, position)
24389     {
24390         Roo.form.Field.superclass.onRender.call(this, ct, position);
24391         
24392         if(!this.el){
24393             var cfg = this.getAutoCreate();
24394             this.el = ct.createChild(cfg, position);
24395         }
24396         
24397         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24398         
24399         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
24400             this.cb.renderer(this.data) :
24401             String.format('{0}',this.data[this.displayField]);
24402         
24403             
24404         this.el.child('div').dom.setAttribute('qtip',
24405                         String.format('{0}',this.data[this.tipField])
24406         );
24407         
24408         this.el.child('img').on('click', this.remove, this);
24409         
24410     },
24411    
24412     remove : function()
24413     {
24414         if(this.cb.disabled){
24415             return;
24416         }
24417         
24418         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
24419             this.cb.items.remove(this);
24420             this.el.child('img').un('click', this.remove, this);
24421             this.el.remove();
24422             this.cb.updateHiddenEl();
24423
24424             this.cb.fireEvent('remove', this.cb, this);
24425         }
24426         
24427     }
24428 });/*
24429  * Based on:
24430  * Ext JS Library 1.1.1
24431  * Copyright(c) 2006-2007, Ext JS, LLC.
24432  *
24433  * Originally Released Under LGPL - original licence link has changed is not relivant.
24434  *
24435  * Fork - LGPL
24436  * <script type="text/javascript">
24437  */
24438 /**
24439  * @class Roo.form.Checkbox
24440  * @extends Roo.form.Field
24441  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
24442  * @constructor
24443  * Creates a new Checkbox
24444  * @param {Object} config Configuration options
24445  */
24446 Roo.form.Checkbox = function(config){
24447     Roo.form.Checkbox.superclass.constructor.call(this, config);
24448     this.addEvents({
24449         /**
24450          * @event check
24451          * Fires when the checkbox is checked or unchecked.
24452              * @param {Roo.form.Checkbox} this This checkbox
24453              * @param {Boolean} checked The new checked value
24454              */
24455         check : true
24456     });
24457 };
24458
24459 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
24460     /**
24461      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24462      */
24463     focusClass : undefined,
24464     /**
24465      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24466      */
24467     fieldClass: "x-form-field",
24468     /**
24469      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24470      */
24471     checked: false,
24472     /**
24473      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24474      * {tag: "input", type: "checkbox", autocomplete: "off"})
24475      */
24476     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
24477     /**
24478      * @cfg {String} boxLabel The text that appears beside the checkbox
24479      */
24480     boxLabel : "",
24481     /**
24482      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
24483      */  
24484     inputValue : '1',
24485     /**
24486      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24487      */
24488      valueOff: '0', // value when not checked..
24489
24490     actionMode : 'viewEl', 
24491     //
24492     // private
24493     itemCls : 'x-menu-check-item x-form-item',
24494     groupClass : 'x-menu-group-item',
24495     inputType : 'hidden',
24496     
24497     
24498     inSetChecked: false, // check that we are not calling self...
24499     
24500     inputElement: false, // real input element?
24501     basedOn: false, // ????
24502     
24503     isFormField: true, // not sure where this is needed!!!!
24504
24505     onResize : function(){
24506         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
24507         if(!this.boxLabel){
24508             this.el.alignTo(this.wrap, 'c-c');
24509         }
24510     },
24511
24512     initEvents : function(){
24513         Roo.form.Checkbox.superclass.initEvents.call(this);
24514         this.el.on("click", this.onClick,  this);
24515         this.el.on("change", this.onClick,  this);
24516     },
24517
24518
24519     getResizeEl : function(){
24520         return this.wrap;
24521     },
24522
24523     getPositionEl : function(){
24524         return this.wrap;
24525     },
24526
24527     // private
24528     onRender : function(ct, position){
24529         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24530         /*
24531         if(this.inputValue !== undefined){
24532             this.el.dom.value = this.inputValue;
24533         }
24534         */
24535         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24536         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24537         var viewEl = this.wrap.createChild({ 
24538             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24539         this.viewEl = viewEl;   
24540         this.wrap.on('click', this.onClick,  this); 
24541         
24542         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24543         this.el.on('propertychange', this.setFromHidden,  this);  //ie
24544         
24545         
24546         
24547         if(this.boxLabel){
24548             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24549         //    viewEl.on('click', this.onClick,  this); 
24550         }
24551         //if(this.checked){
24552             this.setChecked(this.checked);
24553         //}else{
24554             //this.checked = this.el.dom;
24555         //}
24556
24557     },
24558
24559     // private
24560     initValue : Roo.emptyFn,
24561
24562     /**
24563      * Returns the checked state of the checkbox.
24564      * @return {Boolean} True if checked, else false
24565      */
24566     getValue : function(){
24567         if(this.el){
24568             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
24569         }
24570         return this.valueOff;
24571         
24572     },
24573
24574         // private
24575     onClick : function(){ 
24576         if (this.disabled) {
24577             return;
24578         }
24579         this.setChecked(!this.checked);
24580
24581         //if(this.el.dom.checked != this.checked){
24582         //    this.setValue(this.el.dom.checked);
24583        // }
24584     },
24585
24586     /**
24587      * Sets the checked state of the checkbox.
24588      * On is always based on a string comparison between inputValue and the param.
24589      * @param {Boolean/String} value - the value to set 
24590      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
24591      */
24592     setValue : function(v,suppressEvent){
24593         
24594         
24595         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
24596         //if(this.el && this.el.dom){
24597         //    this.el.dom.checked = this.checked;
24598         //    this.el.dom.defaultChecked = this.checked;
24599         //}
24600         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
24601         //this.fireEvent("check", this, this.checked);
24602     },
24603     // private..
24604     setChecked : function(state,suppressEvent)
24605     {
24606         if (this.inSetChecked) {
24607             this.checked = state;
24608             return;
24609         }
24610         
24611     
24612         if(this.wrap){
24613             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
24614         }
24615         this.checked = state;
24616         if(suppressEvent !== true){
24617             this.fireEvent('check', this, state);
24618         }
24619         this.inSetChecked = true;
24620         this.el.dom.value = state ? this.inputValue : this.valueOff;
24621         this.inSetChecked = false;
24622         
24623     },
24624     // handle setting of hidden value by some other method!!?!?
24625     setFromHidden: function()
24626     {
24627         if(!this.el){
24628             return;
24629         }
24630         //console.log("SET FROM HIDDEN");
24631         //alert('setFrom hidden');
24632         this.setValue(this.el.dom.value);
24633     },
24634     
24635     onDestroy : function()
24636     {
24637         if(this.viewEl){
24638             Roo.get(this.viewEl).remove();
24639         }
24640          
24641         Roo.form.Checkbox.superclass.onDestroy.call(this);
24642     },
24643     
24644     setBoxLabel : function(str)
24645     {
24646         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
24647     }
24648
24649 });/*
24650  * Based on:
24651  * Ext JS Library 1.1.1
24652  * Copyright(c) 2006-2007, Ext JS, LLC.
24653  *
24654  * Originally Released Under LGPL - original licence link has changed is not relivant.
24655  *
24656  * Fork - LGPL
24657  * <script type="text/javascript">
24658  */
24659  
24660 /**
24661  * @class Roo.form.Radio
24662  * @extends Roo.form.Checkbox
24663  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
24664  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
24665  * @constructor
24666  * Creates a new Radio
24667  * @param {Object} config Configuration options
24668  */
24669 Roo.form.Radio = function(){
24670     Roo.form.Radio.superclass.constructor.apply(this, arguments);
24671 };
24672 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
24673     inputType: 'radio',
24674
24675     /**
24676      * If this radio is part of a group, it will return the selected value
24677      * @return {String}
24678      */
24679     getGroupValue : function(){
24680         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
24681     },
24682     
24683     
24684     onRender : function(ct, position){
24685         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24686         
24687         if(this.inputValue !== undefined){
24688             this.el.dom.value = this.inputValue;
24689         }
24690          
24691         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24692         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24693         //var viewEl = this.wrap.createChild({ 
24694         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24695         //this.viewEl = viewEl;   
24696         //this.wrap.on('click', this.onClick,  this); 
24697         
24698         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24699         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
24700         
24701         
24702         
24703         if(this.boxLabel){
24704             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24705         //    viewEl.on('click', this.onClick,  this); 
24706         }
24707          if(this.checked){
24708             this.el.dom.checked =   'checked' ;
24709         }
24710          
24711     } 
24712     
24713     
24714 });//<script type="text/javascript">
24715
24716 /*
24717  * Based  Ext JS Library 1.1.1
24718  * Copyright(c) 2006-2007, Ext JS, LLC.
24719  * LGPL
24720  *
24721  */
24722  
24723 /**
24724  * @class Roo.HtmlEditorCore
24725  * @extends Roo.Component
24726  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24727  *
24728  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24729  */
24730
24731 Roo.HtmlEditorCore = function(config){
24732     
24733     
24734     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24735     
24736     
24737     this.addEvents({
24738         /**
24739          * @event initialize
24740          * Fires when the editor is fully initialized (including the iframe)
24741          * @param {Roo.HtmlEditorCore} this
24742          */
24743         initialize: true,
24744         /**
24745          * @event activate
24746          * Fires when the editor is first receives the focus. Any insertion must wait
24747          * until after this event.
24748          * @param {Roo.HtmlEditorCore} this
24749          */
24750         activate: true,
24751          /**
24752          * @event beforesync
24753          * Fires before the textarea is updated with content from the editor iframe. Return false
24754          * to cancel the sync.
24755          * @param {Roo.HtmlEditorCore} this
24756          * @param {String} html
24757          */
24758         beforesync: true,
24759          /**
24760          * @event beforepush
24761          * Fires before the iframe editor is updated with content from the textarea. Return false
24762          * to cancel the push.
24763          * @param {Roo.HtmlEditorCore} this
24764          * @param {String} html
24765          */
24766         beforepush: true,
24767          /**
24768          * @event sync
24769          * Fires when the textarea is updated with content from the editor iframe.
24770          * @param {Roo.HtmlEditorCore} this
24771          * @param {String} html
24772          */
24773         sync: true,
24774          /**
24775          * @event push
24776          * Fires when the iframe editor is updated with content from the textarea.
24777          * @param {Roo.HtmlEditorCore} this
24778          * @param {String} html
24779          */
24780         push: true,
24781         
24782         /**
24783          * @event editorevent
24784          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24785          * @param {Roo.HtmlEditorCore} this
24786          */
24787         editorevent: true
24788         
24789     });
24790     
24791     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24792     
24793     // defaults : white / black...
24794     this.applyBlacklists();
24795     
24796     
24797     
24798 };
24799
24800
24801 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24802
24803
24804      /**
24805      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24806      */
24807     
24808     owner : false,
24809     
24810      /**
24811      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24812      *                        Roo.resizable.
24813      */
24814     resizable : false,
24815      /**
24816      * @cfg {Number} height (in pixels)
24817      */   
24818     height: 300,
24819    /**
24820      * @cfg {Number} width (in pixels)
24821      */   
24822     width: 500,
24823     
24824     /**
24825      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24826      * 
24827      */
24828     stylesheets: false,
24829     
24830     // id of frame..
24831     frameId: false,
24832     
24833     // private properties
24834     validationEvent : false,
24835     deferHeight: true,
24836     initialized : false,
24837     activated : false,
24838     sourceEditMode : false,
24839     onFocus : Roo.emptyFn,
24840     iframePad:3,
24841     hideMode:'offsets',
24842     
24843     clearUp: true,
24844     
24845     // blacklist + whitelisted elements..
24846     black: false,
24847     white: false,
24848      
24849     
24850
24851     /**
24852      * Protected method that will not generally be called directly. It
24853      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24854      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24855      */
24856     getDocMarkup : function(){
24857         // body styles..
24858         var st = '';
24859         
24860         // inherit styels from page...?? 
24861         if (this.stylesheets === false) {
24862             
24863             Roo.get(document.head).select('style').each(function(node) {
24864                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24865             });
24866             
24867             Roo.get(document.head).select('link').each(function(node) { 
24868                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24869             });
24870             
24871         } else if (!this.stylesheets.length) {
24872                 // simple..
24873                 st = '<style type="text/css">' +
24874                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24875                    '</style>';
24876         } else { 
24877             
24878         }
24879         
24880         st +=  '<style type="text/css">' +
24881             'IMG { cursor: pointer } ' +
24882         '</style>';
24883
24884         
24885         return '<html><head>' + st  +
24886             //<style type="text/css">' +
24887             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24888             //'</style>' +
24889             ' </head><body class="roo-htmleditor-body"></body></html>';
24890     },
24891
24892     // private
24893     onRender : function(ct, position)
24894     {
24895         var _t = this;
24896         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24897         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24898         
24899         
24900         this.el.dom.style.border = '0 none';
24901         this.el.dom.setAttribute('tabIndex', -1);
24902         this.el.addClass('x-hidden hide');
24903         
24904         
24905         
24906         if(Roo.isIE){ // fix IE 1px bogus margin
24907             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24908         }
24909        
24910         
24911         this.frameId = Roo.id();
24912         
24913          
24914         
24915         var iframe = this.owner.wrap.createChild({
24916             tag: 'iframe',
24917             cls: 'form-control', // bootstrap..
24918             id: this.frameId,
24919             name: this.frameId,
24920             frameBorder : 'no',
24921             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24922         }, this.el
24923         );
24924         
24925         
24926         this.iframe = iframe.dom;
24927
24928          this.assignDocWin();
24929         
24930         this.doc.designMode = 'on';
24931        
24932         this.doc.open();
24933         this.doc.write(this.getDocMarkup());
24934         this.doc.close();
24935
24936         
24937         var task = { // must defer to wait for browser to be ready
24938             run : function(){
24939                 //console.log("run task?" + this.doc.readyState);
24940                 this.assignDocWin();
24941                 if(this.doc.body || this.doc.readyState == 'complete'){
24942                     try {
24943                         this.doc.designMode="on";
24944                     } catch (e) {
24945                         return;
24946                     }
24947                     Roo.TaskMgr.stop(task);
24948                     this.initEditor.defer(10, this);
24949                 }
24950             },
24951             interval : 10,
24952             duration: 10000,
24953             scope: this
24954         };
24955         Roo.TaskMgr.start(task);
24956
24957     },
24958
24959     // private
24960     onResize : function(w, h)
24961     {
24962          Roo.log('resize: ' +w + ',' + h );
24963         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24964         if(!this.iframe){
24965             return;
24966         }
24967         if(typeof w == 'number'){
24968             
24969             this.iframe.style.width = w + 'px';
24970         }
24971         if(typeof h == 'number'){
24972             
24973             this.iframe.style.height = h + 'px';
24974             if(this.doc){
24975                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24976             }
24977         }
24978         
24979     },
24980
24981     /**
24982      * Toggles the editor between standard and source edit mode.
24983      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24984      */
24985     toggleSourceEdit : function(sourceEditMode){
24986         
24987         this.sourceEditMode = sourceEditMode === true;
24988         
24989         if(this.sourceEditMode){
24990  
24991             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24992             
24993         }else{
24994             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24995             //this.iframe.className = '';
24996             this.deferFocus();
24997         }
24998         //this.setSize(this.owner.wrap.getSize());
24999         //this.fireEvent('editmodechange', this, this.sourceEditMode);
25000     },
25001
25002     
25003   
25004
25005     /**
25006      * Protected method that will not generally be called directly. If you need/want
25007      * custom HTML cleanup, this is the method you should override.
25008      * @param {String} html The HTML to be cleaned
25009      * return {String} The cleaned HTML
25010      */
25011     cleanHtml : function(html){
25012         html = String(html);
25013         if(html.length > 5){
25014             if(Roo.isSafari){ // strip safari nonsense
25015                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25016             }
25017         }
25018         if(html == '&nbsp;'){
25019             html = '';
25020         }
25021         return html;
25022     },
25023
25024     /**
25025      * HTML Editor -> Textarea
25026      * Protected method that will not generally be called directly. Syncs the contents
25027      * of the editor iframe with the textarea.
25028      */
25029     syncValue : function(){
25030         if(this.initialized){
25031             var bd = (this.doc.body || this.doc.documentElement);
25032             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25033             var html = bd.innerHTML;
25034             if(Roo.isSafari){
25035                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25036                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25037                 if(m && m[1]){
25038                     html = '<div style="'+m[0]+'">' + html + '</div>';
25039                 }
25040             }
25041             html = this.cleanHtml(html);
25042             // fix up the special chars.. normaly like back quotes in word...
25043             // however we do not want to do this with chinese..
25044             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
25045                 var cc = b.charCodeAt();
25046                 if (
25047                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25048                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25049                     (cc >= 0xf900 && cc < 0xfb00 )
25050                 ) {
25051                         return b;
25052                 }
25053                 return "&#"+cc+";" 
25054             });
25055             if(this.owner.fireEvent('beforesync', this, html) !== false){
25056                 this.el.dom.value = html;
25057                 this.owner.fireEvent('sync', this, html);
25058             }
25059         }
25060     },
25061
25062     /**
25063      * Protected method that will not generally be called directly. Pushes the value of the textarea
25064      * into the iframe editor.
25065      */
25066     pushValue : function(){
25067         if(this.initialized){
25068             var v = this.el.dom.value.trim();
25069             
25070 //            if(v.length < 1){
25071 //                v = '&#160;';
25072 //            }
25073             
25074             if(this.owner.fireEvent('beforepush', this, v) !== false){
25075                 var d = (this.doc.body || this.doc.documentElement);
25076                 d.innerHTML = v;
25077                 this.cleanUpPaste();
25078                 this.el.dom.value = d.innerHTML;
25079                 this.owner.fireEvent('push', this, v);
25080             }
25081         }
25082     },
25083
25084     // private
25085     deferFocus : function(){
25086         this.focus.defer(10, this);
25087     },
25088
25089     // doc'ed in Field
25090     focus : function(){
25091         if(this.win && !this.sourceEditMode){
25092             this.win.focus();
25093         }else{
25094             this.el.focus();
25095         }
25096     },
25097     
25098     assignDocWin: function()
25099     {
25100         var iframe = this.iframe;
25101         
25102          if(Roo.isIE){
25103             this.doc = iframe.contentWindow.document;
25104             this.win = iframe.contentWindow;
25105         } else {
25106 //            if (!Roo.get(this.frameId)) {
25107 //                return;
25108 //            }
25109 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25110 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25111             
25112             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25113                 return;
25114             }
25115             
25116             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25117             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25118         }
25119     },
25120     
25121     // private
25122     initEditor : function(){
25123         //console.log("INIT EDITOR");
25124         this.assignDocWin();
25125         
25126         
25127         
25128         this.doc.designMode="on";
25129         this.doc.open();
25130         this.doc.write(this.getDocMarkup());
25131         this.doc.close();
25132         
25133         var dbody = (this.doc.body || this.doc.documentElement);
25134         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25135         // this copies styles from the containing element into thsi one..
25136         // not sure why we need all of this..
25137         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25138         
25139         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25140         //ss['background-attachment'] = 'fixed'; // w3c
25141         dbody.bgProperties = 'fixed'; // ie
25142         //Roo.DomHelper.applyStyles(dbody, ss);
25143         Roo.EventManager.on(this.doc, {
25144             //'mousedown': this.onEditorEvent,
25145             'mouseup': this.onEditorEvent,
25146             'dblclick': this.onEditorEvent,
25147             'click': this.onEditorEvent,
25148             'keyup': this.onEditorEvent,
25149             buffer:100,
25150             scope: this
25151         });
25152         if(Roo.isGecko){
25153             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25154         }
25155         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25156             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25157         }
25158         this.initialized = true;
25159
25160         this.owner.fireEvent('initialize', this);
25161         this.pushValue();
25162     },
25163
25164     // private
25165     onDestroy : function(){
25166         
25167         
25168         
25169         if(this.rendered){
25170             
25171             //for (var i =0; i < this.toolbars.length;i++) {
25172             //    // fixme - ask toolbars for heights?
25173             //    this.toolbars[i].onDestroy();
25174            // }
25175             
25176             //this.wrap.dom.innerHTML = '';
25177             //this.wrap.remove();
25178         }
25179     },
25180
25181     // private
25182     onFirstFocus : function(){
25183         
25184         this.assignDocWin();
25185         
25186         
25187         this.activated = true;
25188          
25189     
25190         if(Roo.isGecko){ // prevent silly gecko errors
25191             this.win.focus();
25192             var s = this.win.getSelection();
25193             if(!s.focusNode || s.focusNode.nodeType != 3){
25194                 var r = s.getRangeAt(0);
25195                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25196                 r.collapse(true);
25197                 this.deferFocus();
25198             }
25199             try{
25200                 this.execCmd('useCSS', true);
25201                 this.execCmd('styleWithCSS', false);
25202             }catch(e){}
25203         }
25204         this.owner.fireEvent('activate', this);
25205     },
25206
25207     // private
25208     adjustFont: function(btn){
25209         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25210         //if(Roo.isSafari){ // safari
25211         //    adjust *= 2;
25212        // }
25213         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25214         if(Roo.isSafari){ // safari
25215             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25216             v =  (v < 10) ? 10 : v;
25217             v =  (v > 48) ? 48 : v;
25218             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25219             
25220         }
25221         
25222         
25223         v = Math.max(1, v+adjust);
25224         
25225         this.execCmd('FontSize', v  );
25226     },
25227
25228     onEditorEvent : function(e)
25229     {
25230         this.owner.fireEvent('editorevent', this, e);
25231       //  this.updateToolbar();
25232         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25233     },
25234
25235     insertTag : function(tg)
25236     {
25237         // could be a bit smarter... -> wrap the current selected tRoo..
25238         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
25239             
25240             range = this.createRange(this.getSelection());
25241             var wrappingNode = this.doc.createElement(tg.toLowerCase());
25242             wrappingNode.appendChild(range.extractContents());
25243             range.insertNode(wrappingNode);
25244
25245             return;
25246             
25247             
25248             
25249         }
25250         this.execCmd("formatblock",   tg);
25251         
25252     },
25253     
25254     insertText : function(txt)
25255     {
25256         
25257         
25258         var range = this.createRange();
25259         range.deleteContents();
25260                //alert(Sender.getAttribute('label'));
25261                
25262         range.insertNode(this.doc.createTextNode(txt));
25263     } ,
25264     
25265      
25266
25267     /**
25268      * Executes a Midas editor command on the editor document and performs necessary focus and
25269      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25270      * @param {String} cmd The Midas command
25271      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25272      */
25273     relayCmd : function(cmd, value){
25274         this.win.focus();
25275         this.execCmd(cmd, value);
25276         this.owner.fireEvent('editorevent', this);
25277         //this.updateToolbar();
25278         this.owner.deferFocus();
25279     },
25280
25281     /**
25282      * Executes a Midas editor command directly on the editor document.
25283      * For visual commands, you should use {@link #relayCmd} instead.
25284      * <b>This should only be called after the editor is initialized.</b>
25285      * @param {String} cmd The Midas command
25286      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25287      */
25288     execCmd : function(cmd, value){
25289         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25290         this.syncValue();
25291     },
25292  
25293  
25294    
25295     /**
25296      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25297      * to insert tRoo.
25298      * @param {String} text | dom node.. 
25299      */
25300     insertAtCursor : function(text)
25301     {
25302         
25303         
25304         
25305         if(!this.activated){
25306             return;
25307         }
25308         /*
25309         if(Roo.isIE){
25310             this.win.focus();
25311             var r = this.doc.selection.createRange();
25312             if(r){
25313                 r.collapse(true);
25314                 r.pasteHTML(text);
25315                 this.syncValue();
25316                 this.deferFocus();
25317             
25318             }
25319             return;
25320         }
25321         */
25322         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25323             this.win.focus();
25324             
25325             
25326             // from jquery ui (MIT licenced)
25327             var range, node;
25328             var win = this.win;
25329             
25330             if (win.getSelection && win.getSelection().getRangeAt) {
25331                 range = win.getSelection().getRangeAt(0);
25332                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25333                 range.insertNode(node);
25334             } else if (win.document.selection && win.document.selection.createRange) {
25335                 // no firefox support
25336                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25337                 win.document.selection.createRange().pasteHTML(txt);
25338             } else {
25339                 // no firefox support
25340                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25341                 this.execCmd('InsertHTML', txt);
25342             } 
25343             
25344             this.syncValue();
25345             
25346             this.deferFocus();
25347         }
25348     },
25349  // private
25350     mozKeyPress : function(e){
25351         if(e.ctrlKey){
25352             var c = e.getCharCode(), cmd;
25353           
25354             if(c > 0){
25355                 c = String.fromCharCode(c).toLowerCase();
25356                 switch(c){
25357                     case 'b':
25358                         cmd = 'bold';
25359                         break;
25360                     case 'i':
25361                         cmd = 'italic';
25362                         break;
25363                     
25364                     case 'u':
25365                         cmd = 'underline';
25366                         break;
25367                     
25368                     case 'v':
25369                         this.cleanUpPaste.defer(100, this);
25370                         return;
25371                         
25372                 }
25373                 if(cmd){
25374                     this.win.focus();
25375                     this.execCmd(cmd);
25376                     this.deferFocus();
25377                     e.preventDefault();
25378                 }
25379                 
25380             }
25381         }
25382     },
25383
25384     // private
25385     fixKeys : function(){ // load time branching for fastest keydown performance
25386         if(Roo.isIE){
25387             return function(e){
25388                 var k = e.getKey(), r;
25389                 if(k == e.TAB){
25390                     e.stopEvent();
25391                     r = this.doc.selection.createRange();
25392                     if(r){
25393                         r.collapse(true);
25394                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25395                         this.deferFocus();
25396                     }
25397                     return;
25398                 }
25399                 
25400                 if(k == e.ENTER){
25401                     r = this.doc.selection.createRange();
25402                     if(r){
25403                         var target = r.parentElement();
25404                         if(!target || target.tagName.toLowerCase() != 'li'){
25405                             e.stopEvent();
25406                             r.pasteHTML('<br />');
25407                             r.collapse(false);
25408                             r.select();
25409                         }
25410                     }
25411                 }
25412                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25413                     this.cleanUpPaste.defer(100, this);
25414                     return;
25415                 }
25416                 
25417                 
25418             };
25419         }else if(Roo.isOpera){
25420             return function(e){
25421                 var k = e.getKey();
25422                 if(k == e.TAB){
25423                     e.stopEvent();
25424                     this.win.focus();
25425                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25426                     this.deferFocus();
25427                 }
25428                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25429                     this.cleanUpPaste.defer(100, this);
25430                     return;
25431                 }
25432                 
25433             };
25434         }else if(Roo.isSafari){
25435             return function(e){
25436                 var k = e.getKey();
25437                 
25438                 if(k == e.TAB){
25439                     e.stopEvent();
25440                     this.execCmd('InsertText','\t');
25441                     this.deferFocus();
25442                     return;
25443                 }
25444                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25445                     this.cleanUpPaste.defer(100, this);
25446                     return;
25447                 }
25448                 
25449              };
25450         }
25451     }(),
25452     
25453     getAllAncestors: function()
25454     {
25455         var p = this.getSelectedNode();
25456         var a = [];
25457         if (!p) {
25458             a.push(p); // push blank onto stack..
25459             p = this.getParentElement();
25460         }
25461         
25462         
25463         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25464             a.push(p);
25465             p = p.parentNode;
25466         }
25467         a.push(this.doc.body);
25468         return a;
25469     },
25470     lastSel : false,
25471     lastSelNode : false,
25472     
25473     
25474     getSelection : function() 
25475     {
25476         this.assignDocWin();
25477         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25478     },
25479     
25480     getSelectedNode: function() 
25481     {
25482         // this may only work on Gecko!!!
25483         
25484         // should we cache this!!!!
25485         
25486         
25487         
25488          
25489         var range = this.createRange(this.getSelection()).cloneRange();
25490         
25491         if (Roo.isIE) {
25492             var parent = range.parentElement();
25493             while (true) {
25494                 var testRange = range.duplicate();
25495                 testRange.moveToElementText(parent);
25496                 if (testRange.inRange(range)) {
25497                     break;
25498                 }
25499                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25500                     break;
25501                 }
25502                 parent = parent.parentElement;
25503             }
25504             return parent;
25505         }
25506         
25507         // is ancestor a text element.
25508         var ac =  range.commonAncestorContainer;
25509         if (ac.nodeType == 3) {
25510             ac = ac.parentNode;
25511         }
25512         
25513         var ar = ac.childNodes;
25514          
25515         var nodes = [];
25516         var other_nodes = [];
25517         var has_other_nodes = false;
25518         for (var i=0;i<ar.length;i++) {
25519             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25520                 continue;
25521             }
25522             // fullly contained node.
25523             
25524             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25525                 nodes.push(ar[i]);
25526                 continue;
25527             }
25528             
25529             // probably selected..
25530             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25531                 other_nodes.push(ar[i]);
25532                 continue;
25533             }
25534             // outer..
25535             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25536                 continue;
25537             }
25538             
25539             
25540             has_other_nodes = true;
25541         }
25542         if (!nodes.length && other_nodes.length) {
25543             nodes= other_nodes;
25544         }
25545         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25546             return false;
25547         }
25548         
25549         return nodes[0];
25550     },
25551     createRange: function(sel)
25552     {
25553         // this has strange effects when using with 
25554         // top toolbar - not sure if it's a great idea.
25555         //this.editor.contentWindow.focus();
25556         if (typeof sel != "undefined") {
25557             try {
25558                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25559             } catch(e) {
25560                 return this.doc.createRange();
25561             }
25562         } else {
25563             return this.doc.createRange();
25564         }
25565     },
25566     getParentElement: function()
25567     {
25568         
25569         this.assignDocWin();
25570         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25571         
25572         var range = this.createRange(sel);
25573          
25574         try {
25575             var p = range.commonAncestorContainer;
25576             while (p.nodeType == 3) { // text node
25577                 p = p.parentNode;
25578             }
25579             return p;
25580         } catch (e) {
25581             return null;
25582         }
25583     
25584     },
25585     /***
25586      *
25587      * Range intersection.. the hard stuff...
25588      *  '-1' = before
25589      *  '0' = hits..
25590      *  '1' = after.
25591      *         [ -- selected range --- ]
25592      *   [fail]                        [fail]
25593      *
25594      *    basically..
25595      *      if end is before start or  hits it. fail.
25596      *      if start is after end or hits it fail.
25597      *
25598      *   if either hits (but other is outside. - then it's not 
25599      *   
25600      *    
25601      **/
25602     
25603     
25604     // @see http://www.thismuchiknow.co.uk/?p=64.
25605     rangeIntersectsNode : function(range, node)
25606     {
25607         var nodeRange = node.ownerDocument.createRange();
25608         try {
25609             nodeRange.selectNode(node);
25610         } catch (e) {
25611             nodeRange.selectNodeContents(node);
25612         }
25613     
25614         var rangeStartRange = range.cloneRange();
25615         rangeStartRange.collapse(true);
25616     
25617         var rangeEndRange = range.cloneRange();
25618         rangeEndRange.collapse(false);
25619     
25620         var nodeStartRange = nodeRange.cloneRange();
25621         nodeStartRange.collapse(true);
25622     
25623         var nodeEndRange = nodeRange.cloneRange();
25624         nodeEndRange.collapse(false);
25625     
25626         return rangeStartRange.compareBoundaryPoints(
25627                  Range.START_TO_START, nodeEndRange) == -1 &&
25628                rangeEndRange.compareBoundaryPoints(
25629                  Range.START_TO_START, nodeStartRange) == 1;
25630         
25631          
25632     },
25633     rangeCompareNode : function(range, node)
25634     {
25635         var nodeRange = node.ownerDocument.createRange();
25636         try {
25637             nodeRange.selectNode(node);
25638         } catch (e) {
25639             nodeRange.selectNodeContents(node);
25640         }
25641         
25642         
25643         range.collapse(true);
25644     
25645         nodeRange.collapse(true);
25646      
25647         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25648         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25649          
25650         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25651         
25652         var nodeIsBefore   =  ss == 1;
25653         var nodeIsAfter    = ee == -1;
25654         
25655         if (nodeIsBefore && nodeIsAfter) {
25656             return 0; // outer
25657         }
25658         if (!nodeIsBefore && nodeIsAfter) {
25659             return 1; //right trailed.
25660         }
25661         
25662         if (nodeIsBefore && !nodeIsAfter) {
25663             return 2;  // left trailed.
25664         }
25665         // fully contined.
25666         return 3;
25667     },
25668
25669     // private? - in a new class?
25670     cleanUpPaste :  function()
25671     {
25672         // cleans up the whole document..
25673         Roo.log('cleanuppaste');
25674         
25675         this.cleanUpChildren(this.doc.body);
25676         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25677         if (clean != this.doc.body.innerHTML) {
25678             this.doc.body.innerHTML = clean;
25679         }
25680         
25681     },
25682     
25683     cleanWordChars : function(input) {// change the chars to hex code
25684         var he = Roo.HtmlEditorCore;
25685         
25686         var output = input;
25687         Roo.each(he.swapCodes, function(sw) { 
25688             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25689             
25690             output = output.replace(swapper, sw[1]);
25691         });
25692         
25693         return output;
25694     },
25695     
25696     
25697     cleanUpChildren : function (n)
25698     {
25699         if (!n.childNodes.length) {
25700             return;
25701         }
25702         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25703            this.cleanUpChild(n.childNodes[i]);
25704         }
25705     },
25706     
25707     
25708         
25709     
25710     cleanUpChild : function (node)
25711     {
25712         var ed = this;
25713         //console.log(node);
25714         if (node.nodeName == "#text") {
25715             // clean up silly Windows -- stuff?
25716             return; 
25717         }
25718         if (node.nodeName == "#comment") {
25719             node.parentNode.removeChild(node);
25720             // clean up silly Windows -- stuff?
25721             return; 
25722         }
25723         var lcname = node.tagName.toLowerCase();
25724         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25725         // whitelist of tags..
25726         
25727         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25728             // remove node.
25729             node.parentNode.removeChild(node);
25730             return;
25731             
25732         }
25733         
25734         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25735         
25736         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25737         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25738         
25739         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25740         //    remove_keep_children = true;
25741         //}
25742         
25743         if (remove_keep_children) {
25744             this.cleanUpChildren(node);
25745             // inserts everything just before this node...
25746             while (node.childNodes.length) {
25747                 var cn = node.childNodes[0];
25748                 node.removeChild(cn);
25749                 node.parentNode.insertBefore(cn, node);
25750             }
25751             node.parentNode.removeChild(node);
25752             return;
25753         }
25754         
25755         if (!node.attributes || !node.attributes.length) {
25756             this.cleanUpChildren(node);
25757             return;
25758         }
25759         
25760         function cleanAttr(n,v)
25761         {
25762             
25763             if (v.match(/^\./) || v.match(/^\//)) {
25764                 return;
25765             }
25766             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
25767                 return;
25768             }
25769             if (v.match(/^#/)) {
25770                 return;
25771             }
25772 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25773             node.removeAttribute(n);
25774             
25775         }
25776         
25777         var cwhite = this.cwhite;
25778         var cblack = this.cblack;
25779             
25780         function cleanStyle(n,v)
25781         {
25782             if (v.match(/expression/)) { //XSS?? should we even bother..
25783                 node.removeAttribute(n);
25784                 return;
25785             }
25786             
25787             var parts = v.split(/;/);
25788             var clean = [];
25789             
25790             Roo.each(parts, function(p) {
25791                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25792                 if (!p.length) {
25793                     return true;
25794                 }
25795                 var l = p.split(':').shift().replace(/\s+/g,'');
25796                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25797                 
25798                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25799 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25800                     //node.removeAttribute(n);
25801                     return true;
25802                 }
25803                 //Roo.log()
25804                 // only allow 'c whitelisted system attributes'
25805                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25806 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25807                     //node.removeAttribute(n);
25808                     return true;
25809                 }
25810                 
25811                 
25812                  
25813                 
25814                 clean.push(p);
25815                 return true;
25816             });
25817             if (clean.length) { 
25818                 node.setAttribute(n, clean.join(';'));
25819             } else {
25820                 node.removeAttribute(n);
25821             }
25822             
25823         }
25824         
25825         
25826         for (var i = node.attributes.length-1; i > -1 ; i--) {
25827             var a = node.attributes[i];
25828             //console.log(a);
25829             
25830             if (a.name.toLowerCase().substr(0,2)=='on')  {
25831                 node.removeAttribute(a.name);
25832                 continue;
25833             }
25834             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25835                 node.removeAttribute(a.name);
25836                 continue;
25837             }
25838             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25839                 cleanAttr(a.name,a.value); // fixme..
25840                 continue;
25841             }
25842             if (a.name == 'style') {
25843                 cleanStyle(a.name,a.value);
25844                 continue;
25845             }
25846             /// clean up MS crap..
25847             // tecnically this should be a list of valid class'es..
25848             
25849             
25850             if (a.name == 'class') {
25851                 if (a.value.match(/^Mso/)) {
25852                     node.className = '';
25853                 }
25854                 
25855                 if (a.value.match(/body/)) {
25856                     node.className = '';
25857                 }
25858                 continue;
25859             }
25860             
25861             // style cleanup!?
25862             // class cleanup?
25863             
25864         }
25865         
25866         
25867         this.cleanUpChildren(node);
25868         
25869         
25870     },
25871     
25872     /**
25873      * Clean up MS wordisms...
25874      */
25875     cleanWord : function(node)
25876     {
25877         
25878         
25879         if (!node) {
25880             this.cleanWord(this.doc.body);
25881             return;
25882         }
25883         if (node.nodeName == "#text") {
25884             // clean up silly Windows -- stuff?
25885             return; 
25886         }
25887         if (node.nodeName == "#comment") {
25888             node.parentNode.removeChild(node);
25889             // clean up silly Windows -- stuff?
25890             return; 
25891         }
25892         
25893         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25894             node.parentNode.removeChild(node);
25895             return;
25896         }
25897         
25898         // remove - but keep children..
25899         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
25900             while (node.childNodes.length) {
25901                 var cn = node.childNodes[0];
25902                 node.removeChild(cn);
25903                 node.parentNode.insertBefore(cn, node);
25904             }
25905             node.parentNode.removeChild(node);
25906             this.iterateChildren(node, this.cleanWord);
25907             return;
25908         }
25909         // clean styles
25910         if (node.className.length) {
25911             
25912             var cn = node.className.split(/\W+/);
25913             var cna = [];
25914             Roo.each(cn, function(cls) {
25915                 if (cls.match(/Mso[a-zA-Z]+/)) {
25916                     return;
25917                 }
25918                 cna.push(cls);
25919             });
25920             node.className = cna.length ? cna.join(' ') : '';
25921             if (!cna.length) {
25922                 node.removeAttribute("class");
25923             }
25924         }
25925         
25926         if (node.hasAttribute("lang")) {
25927             node.removeAttribute("lang");
25928         }
25929         
25930         if (node.hasAttribute("style")) {
25931             
25932             var styles = node.getAttribute("style").split(";");
25933             var nstyle = [];
25934             Roo.each(styles, function(s) {
25935                 if (!s.match(/:/)) {
25936                     return;
25937                 }
25938                 var kv = s.split(":");
25939                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25940                     return;
25941                 }
25942                 // what ever is left... we allow.
25943                 nstyle.push(s);
25944             });
25945             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25946             if (!nstyle.length) {
25947                 node.removeAttribute('style');
25948             }
25949         }
25950         this.iterateChildren(node, this.cleanWord);
25951         
25952         
25953         
25954     },
25955     /**
25956      * iterateChildren of a Node, calling fn each time, using this as the scole..
25957      * @param {DomNode} node node to iterate children of.
25958      * @param {Function} fn method of this class to call on each item.
25959      */
25960     iterateChildren : function(node, fn)
25961     {
25962         if (!node.childNodes.length) {
25963                 return;
25964         }
25965         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25966            fn.call(this, node.childNodes[i])
25967         }
25968     },
25969     
25970     
25971     /**
25972      * cleanTableWidths.
25973      *
25974      * Quite often pasting from word etc.. results in tables with column and widths.
25975      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25976      *
25977      */
25978     cleanTableWidths : function(node)
25979     {
25980          
25981          
25982         if (!node) {
25983             this.cleanTableWidths(this.doc.body);
25984             return;
25985         }
25986         
25987         // ignore list...
25988         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25989             return; 
25990         }
25991         Roo.log(node.tagName);
25992         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25993             this.iterateChildren(node, this.cleanTableWidths);
25994             return;
25995         }
25996         if (node.hasAttribute('width')) {
25997             node.removeAttribute('width');
25998         }
25999         
26000          
26001         if (node.hasAttribute("style")) {
26002             // pretty basic...
26003             
26004             var styles = node.getAttribute("style").split(";");
26005             var nstyle = [];
26006             Roo.each(styles, function(s) {
26007                 if (!s.match(/:/)) {
26008                     return;
26009                 }
26010                 var kv = s.split(":");
26011                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26012                     return;
26013                 }
26014                 // what ever is left... we allow.
26015                 nstyle.push(s);
26016             });
26017             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26018             if (!nstyle.length) {
26019                 node.removeAttribute('style');
26020             }
26021         }
26022         
26023         this.iterateChildren(node, this.cleanTableWidths);
26024         
26025         
26026     },
26027     
26028     
26029     
26030     
26031     domToHTML : function(currentElement, depth, nopadtext) {
26032         
26033         depth = depth || 0;
26034         nopadtext = nopadtext || false;
26035     
26036         if (!currentElement) {
26037             return this.domToHTML(this.doc.body);
26038         }
26039         
26040         //Roo.log(currentElement);
26041         var j;
26042         var allText = false;
26043         var nodeName = currentElement.nodeName;
26044         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26045         
26046         if  (nodeName == '#text') {
26047             
26048             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26049         }
26050         
26051         
26052         var ret = '';
26053         if (nodeName != 'BODY') {
26054              
26055             var i = 0;
26056             // Prints the node tagName, such as <A>, <IMG>, etc
26057             if (tagName) {
26058                 var attr = [];
26059                 for(i = 0; i < currentElement.attributes.length;i++) {
26060                     // quoting?
26061                     var aname = currentElement.attributes.item(i).name;
26062                     if (!currentElement.attributes.item(i).value.length) {
26063                         continue;
26064                     }
26065                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26066                 }
26067                 
26068                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26069             } 
26070             else {
26071                 
26072                 // eack
26073             }
26074         } else {
26075             tagName = false;
26076         }
26077         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26078             return ret;
26079         }
26080         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26081             nopadtext = true;
26082         }
26083         
26084         
26085         // Traverse the tree
26086         i = 0;
26087         var currentElementChild = currentElement.childNodes.item(i);
26088         var allText = true;
26089         var innerHTML  = '';
26090         lastnode = '';
26091         while (currentElementChild) {
26092             // Formatting code (indent the tree so it looks nice on the screen)
26093             var nopad = nopadtext;
26094             if (lastnode == 'SPAN') {
26095                 nopad  = true;
26096             }
26097             // text
26098             if  (currentElementChild.nodeName == '#text') {
26099                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26100                 toadd = nopadtext ? toadd : toadd.trim();
26101                 if (!nopad && toadd.length > 80) {
26102                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
26103                 }
26104                 innerHTML  += toadd;
26105                 
26106                 i++;
26107                 currentElementChild = currentElement.childNodes.item(i);
26108                 lastNode = '';
26109                 continue;
26110             }
26111             allText = false;
26112             
26113             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
26114                 
26115             // Recursively traverse the tree structure of the child node
26116             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
26117             lastnode = currentElementChild.nodeName;
26118             i++;
26119             currentElementChild=currentElement.childNodes.item(i);
26120         }
26121         
26122         ret += innerHTML;
26123         
26124         if (!allText) {
26125                 // The remaining code is mostly for formatting the tree
26126             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
26127         }
26128         
26129         
26130         if (tagName) {
26131             ret+= "</"+tagName+">";
26132         }
26133         return ret;
26134         
26135     },
26136         
26137     applyBlacklists : function()
26138     {
26139         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
26140         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
26141         
26142         this.white = [];
26143         this.black = [];
26144         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26145             if (b.indexOf(tag) > -1) {
26146                 return;
26147             }
26148             this.white.push(tag);
26149             
26150         }, this);
26151         
26152         Roo.each(w, function(tag) {
26153             if (b.indexOf(tag) > -1) {
26154                 return;
26155             }
26156             if (this.white.indexOf(tag) > -1) {
26157                 return;
26158             }
26159             this.white.push(tag);
26160             
26161         }, this);
26162         
26163         
26164         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26165             if (w.indexOf(tag) > -1) {
26166                 return;
26167             }
26168             this.black.push(tag);
26169             
26170         }, this);
26171         
26172         Roo.each(b, function(tag) {
26173             if (w.indexOf(tag) > -1) {
26174                 return;
26175             }
26176             if (this.black.indexOf(tag) > -1) {
26177                 return;
26178             }
26179             this.black.push(tag);
26180             
26181         }, this);
26182         
26183         
26184         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
26185         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
26186         
26187         this.cwhite = [];
26188         this.cblack = [];
26189         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26190             if (b.indexOf(tag) > -1) {
26191                 return;
26192             }
26193             this.cwhite.push(tag);
26194             
26195         }, this);
26196         
26197         Roo.each(w, function(tag) {
26198             if (b.indexOf(tag) > -1) {
26199                 return;
26200             }
26201             if (this.cwhite.indexOf(tag) > -1) {
26202                 return;
26203             }
26204             this.cwhite.push(tag);
26205             
26206         }, this);
26207         
26208         
26209         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26210             if (w.indexOf(tag) > -1) {
26211                 return;
26212             }
26213             this.cblack.push(tag);
26214             
26215         }, this);
26216         
26217         Roo.each(b, function(tag) {
26218             if (w.indexOf(tag) > -1) {
26219                 return;
26220             }
26221             if (this.cblack.indexOf(tag) > -1) {
26222                 return;
26223             }
26224             this.cblack.push(tag);
26225             
26226         }, this);
26227     },
26228     
26229     setStylesheets : function(stylesheets)
26230     {
26231         if(typeof(stylesheets) == 'string'){
26232             Roo.get(this.iframe.contentDocument.head).createChild({
26233                 tag : 'link',
26234                 rel : 'stylesheet',
26235                 type : 'text/css',
26236                 href : stylesheets
26237             });
26238             
26239             return;
26240         }
26241         var _this = this;
26242      
26243         Roo.each(stylesheets, function(s) {
26244             if(!s.length){
26245                 return;
26246             }
26247             
26248             Roo.get(_this.iframe.contentDocument.head).createChild({
26249                 tag : 'link',
26250                 rel : 'stylesheet',
26251                 type : 'text/css',
26252                 href : s
26253             });
26254         });
26255
26256         
26257     },
26258     
26259     removeStylesheets : function()
26260     {
26261         var _this = this;
26262         
26263         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26264             s.remove();
26265         });
26266     }
26267     
26268     // hide stuff that is not compatible
26269     /**
26270      * @event blur
26271      * @hide
26272      */
26273     /**
26274      * @event change
26275      * @hide
26276      */
26277     /**
26278      * @event focus
26279      * @hide
26280      */
26281     /**
26282      * @event specialkey
26283      * @hide
26284      */
26285     /**
26286      * @cfg {String} fieldClass @hide
26287      */
26288     /**
26289      * @cfg {String} focusClass @hide
26290      */
26291     /**
26292      * @cfg {String} autoCreate @hide
26293      */
26294     /**
26295      * @cfg {String} inputType @hide
26296      */
26297     /**
26298      * @cfg {String} invalidClass @hide
26299      */
26300     /**
26301      * @cfg {String} invalidText @hide
26302      */
26303     /**
26304      * @cfg {String} msgFx @hide
26305      */
26306     /**
26307      * @cfg {String} validateOnBlur @hide
26308      */
26309 });
26310
26311 Roo.HtmlEditorCore.white = [
26312         'area', 'br', 'img', 'input', 'hr', 'wbr',
26313         
26314        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26315        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26316        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26317        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26318        'table',   'ul',         'xmp', 
26319        
26320        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26321       'thead',   'tr', 
26322      
26323       'dir', 'menu', 'ol', 'ul', 'dl',
26324        
26325       'embed',  'object'
26326 ];
26327
26328
26329 Roo.HtmlEditorCore.black = [
26330     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26331         'applet', // 
26332         'base',   'basefont', 'bgsound', 'blink',  'body', 
26333         'frame',  'frameset', 'head',    'html',   'ilayer', 
26334         'iframe', 'layer',  'link',     'meta',    'object',   
26335         'script', 'style' ,'title',  'xml' // clean later..
26336 ];
26337 Roo.HtmlEditorCore.clean = [
26338     'script', 'style', 'title', 'xml'
26339 ];
26340 Roo.HtmlEditorCore.remove = [
26341     'font'
26342 ];
26343 // attributes..
26344
26345 Roo.HtmlEditorCore.ablack = [
26346     'on'
26347 ];
26348     
26349 Roo.HtmlEditorCore.aclean = [ 
26350     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26351 ];
26352
26353 // protocols..
26354 Roo.HtmlEditorCore.pwhite= [
26355         'http',  'https',  'mailto'
26356 ];
26357
26358 // white listed style attributes.
26359 Roo.HtmlEditorCore.cwhite= [
26360       //  'text-align', /// default is to allow most things..
26361       
26362          
26363 //        'font-size'//??
26364 ];
26365
26366 // black listed style attributes.
26367 Roo.HtmlEditorCore.cblack= [
26368       //  'font-size' -- this can be set by the project 
26369 ];
26370
26371
26372 Roo.HtmlEditorCore.swapCodes   =[ 
26373     [    8211, "--" ], 
26374     [    8212, "--" ], 
26375     [    8216,  "'" ],  
26376     [    8217, "'" ],  
26377     [    8220, '"' ],  
26378     [    8221, '"' ],  
26379     [    8226, "*" ],  
26380     [    8230, "..." ]
26381 ]; 
26382
26383     //<script type="text/javascript">
26384
26385 /*
26386  * Ext JS Library 1.1.1
26387  * Copyright(c) 2006-2007, Ext JS, LLC.
26388  * Licence LGPL
26389  * 
26390  */
26391  
26392  
26393 Roo.form.HtmlEditor = function(config){
26394     
26395     
26396     
26397     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
26398     
26399     if (!this.toolbars) {
26400         this.toolbars = [];
26401     }
26402     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26403     
26404     
26405 };
26406
26407 /**
26408  * @class Roo.form.HtmlEditor
26409  * @extends Roo.form.Field
26410  * Provides a lightweight HTML Editor component.
26411  *
26412  * This has been tested on Fireforx / Chrome.. IE may not be so great..
26413  * 
26414  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
26415  * supported by this editor.</b><br/><br/>
26416  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
26417  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
26418  */
26419 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
26420     /**
26421      * @cfg {Boolean} clearUp
26422      */
26423     clearUp : true,
26424       /**
26425      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26426      */
26427     toolbars : false,
26428    
26429      /**
26430      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26431      *                        Roo.resizable.
26432      */
26433     resizable : false,
26434      /**
26435      * @cfg {Number} height (in pixels)
26436      */   
26437     height: 300,
26438    /**
26439      * @cfg {Number} width (in pixels)
26440      */   
26441     width: 500,
26442     
26443     /**
26444      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26445      * 
26446      */
26447     stylesheets: false,
26448     
26449     
26450      /**
26451      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
26452      * 
26453      */
26454     cblack: false,
26455     /**
26456      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
26457      * 
26458      */
26459     cwhite: false,
26460     
26461      /**
26462      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
26463      * 
26464      */
26465     black: false,
26466     /**
26467      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
26468      * 
26469      */
26470     white: false,
26471     
26472     // id of frame..
26473     frameId: false,
26474     
26475     // private properties
26476     validationEvent : false,
26477     deferHeight: true,
26478     initialized : false,
26479     activated : false,
26480     
26481     onFocus : Roo.emptyFn,
26482     iframePad:3,
26483     hideMode:'offsets',
26484     
26485     actionMode : 'container', // defaults to hiding it...
26486     
26487     defaultAutoCreate : { // modified by initCompnoent..
26488         tag: "textarea",
26489         style:"width:500px;height:300px;",
26490         autocomplete: "new-password"
26491     },
26492
26493     // private
26494     initComponent : function(){
26495         this.addEvents({
26496             /**
26497              * @event initialize
26498              * Fires when the editor is fully initialized (including the iframe)
26499              * @param {HtmlEditor} this
26500              */
26501             initialize: true,
26502             /**
26503              * @event activate
26504              * Fires when the editor is first receives the focus. Any insertion must wait
26505              * until after this event.
26506              * @param {HtmlEditor} this
26507              */
26508             activate: true,
26509              /**
26510              * @event beforesync
26511              * Fires before the textarea is updated with content from the editor iframe. Return false
26512              * to cancel the sync.
26513              * @param {HtmlEditor} this
26514              * @param {String} html
26515              */
26516             beforesync: true,
26517              /**
26518              * @event beforepush
26519              * Fires before the iframe editor is updated with content from the textarea. Return false
26520              * to cancel the push.
26521              * @param {HtmlEditor} this
26522              * @param {String} html
26523              */
26524             beforepush: true,
26525              /**
26526              * @event sync
26527              * Fires when the textarea is updated with content from the editor iframe.
26528              * @param {HtmlEditor} this
26529              * @param {String} html
26530              */
26531             sync: true,
26532              /**
26533              * @event push
26534              * Fires when the iframe editor is updated with content from the textarea.
26535              * @param {HtmlEditor} this
26536              * @param {String} html
26537              */
26538             push: true,
26539              /**
26540              * @event editmodechange
26541              * Fires when the editor switches edit modes
26542              * @param {HtmlEditor} this
26543              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26544              */
26545             editmodechange: true,
26546             /**
26547              * @event editorevent
26548              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26549              * @param {HtmlEditor} this
26550              */
26551             editorevent: true,
26552             /**
26553              * @event firstfocus
26554              * Fires when on first focus - needed by toolbars..
26555              * @param {HtmlEditor} this
26556              */
26557             firstfocus: true,
26558             /**
26559              * @event autosave
26560              * Auto save the htmlEditor value as a file into Events
26561              * @param {HtmlEditor} this
26562              */
26563             autosave: true,
26564             /**
26565              * @event savedpreview
26566              * preview the saved version of htmlEditor
26567              * @param {HtmlEditor} this
26568              */
26569             savedpreview: true,
26570             
26571             /**
26572             * @event stylesheetsclick
26573             * Fires when press the Sytlesheets button
26574             * @param {Roo.HtmlEditorCore} this
26575             */
26576             stylesheetsclick: true
26577         });
26578         this.defaultAutoCreate =  {
26579             tag: "textarea",
26580             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
26581             autocomplete: "new-password"
26582         };
26583     },
26584
26585     /**
26586      * Protected method that will not generally be called directly. It
26587      * is called when the editor creates its toolbar. Override this method if you need to
26588      * add custom toolbar buttons.
26589      * @param {HtmlEditor} editor
26590      */
26591     createToolbar : function(editor){
26592         Roo.log("create toolbars");
26593         if (!editor.toolbars || !editor.toolbars.length) {
26594             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
26595         }
26596         
26597         for (var i =0 ; i < editor.toolbars.length;i++) {
26598             editor.toolbars[i] = Roo.factory(
26599                     typeof(editor.toolbars[i]) == 'string' ?
26600                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
26601                 Roo.form.HtmlEditor);
26602             editor.toolbars[i].init(editor);
26603         }
26604          
26605         
26606     },
26607
26608      
26609     // private
26610     onRender : function(ct, position)
26611     {
26612         var _t = this;
26613         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
26614         
26615         this.wrap = this.el.wrap({
26616             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26617         });
26618         
26619         this.editorcore.onRender(ct, position);
26620          
26621         if (this.resizable) {
26622             this.resizeEl = new Roo.Resizable(this.wrap, {
26623                 pinned : true,
26624                 wrap: true,
26625                 dynamic : true,
26626                 minHeight : this.height,
26627                 height: this.height,
26628                 handles : this.resizable,
26629                 width: this.width,
26630                 listeners : {
26631                     resize : function(r, w, h) {
26632                         _t.onResize(w,h); // -something
26633                     }
26634                 }
26635             });
26636             
26637         }
26638         this.createToolbar(this);
26639        
26640         
26641         if(!this.width){
26642             this.setSize(this.wrap.getSize());
26643         }
26644         if (this.resizeEl) {
26645             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26646             // should trigger onReize..
26647         }
26648         
26649         this.keyNav = new Roo.KeyNav(this.el, {
26650             
26651             "tab" : function(e){
26652                 e.preventDefault();
26653                 
26654                 var value = this.getValue();
26655                 
26656                 var start = this.el.dom.selectionStart;
26657                 var end = this.el.dom.selectionEnd;
26658                 
26659                 if(!e.shiftKey){
26660                     
26661                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
26662                     this.el.dom.setSelectionRange(end + 1, end + 1);
26663                     return;
26664                 }
26665                 
26666                 var f = value.substring(0, start).split("\t");
26667                 
26668                 if(f.pop().length != 0){
26669                     return;
26670                 }
26671                 
26672                 this.setValue(f.join("\t") + value.substring(end));
26673                 this.el.dom.setSelectionRange(start - 1, start - 1);
26674                 
26675             },
26676             
26677             "home" : function(e){
26678                 e.preventDefault();
26679                 
26680                 var curr = this.el.dom.selectionStart;
26681                 var lines = this.getValue().split("\n");
26682                 
26683                 if(!lines.length){
26684                     return;
26685                 }
26686                 
26687                 if(e.ctrlKey){
26688                     this.el.dom.setSelectionRange(0, 0);
26689                     return;
26690                 }
26691                 
26692                 var pos = 0;
26693                 
26694                 for (var i = 0; i < lines.length;i++) {
26695                     pos += lines[i].length;
26696                     
26697                     if(i != 0){
26698                         pos += 1;
26699                     }
26700                     
26701                     if(pos < curr){
26702                         continue;
26703                     }
26704                     
26705                     pos -= lines[i].length;
26706                     
26707                     break;
26708                 }
26709                 
26710                 if(!e.shiftKey){
26711                     this.el.dom.setSelectionRange(pos, pos);
26712                     return;
26713                 }
26714                 
26715                 this.el.dom.selectionStart = pos;
26716                 this.el.dom.selectionEnd = curr;
26717             },
26718             
26719             "end" : function(e){
26720                 e.preventDefault();
26721                 
26722                 var curr = this.el.dom.selectionStart;
26723                 var lines = this.getValue().split("\n");
26724                 
26725                 if(!lines.length){
26726                     return;
26727                 }
26728                 
26729                 if(e.ctrlKey){
26730                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
26731                     return;
26732                 }
26733                 
26734                 var pos = 0;
26735                 
26736                 for (var i = 0; i < lines.length;i++) {
26737                     
26738                     pos += lines[i].length;
26739                     
26740                     if(i != 0){
26741                         pos += 1;
26742                     }
26743                     
26744                     if(pos < curr){
26745                         continue;
26746                     }
26747                     
26748                     break;
26749                 }
26750                 
26751                 if(!e.shiftKey){
26752                     this.el.dom.setSelectionRange(pos, pos);
26753                     return;
26754                 }
26755                 
26756                 this.el.dom.selectionStart = curr;
26757                 this.el.dom.selectionEnd = pos;
26758             },
26759
26760             scope : this,
26761
26762             doRelay : function(foo, bar, hname){
26763                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
26764             },
26765
26766             forceKeyDown: true
26767         });
26768         
26769 //        if(this.autosave && this.w){
26770 //            this.autoSaveFn = setInterval(this.autosave, 1000);
26771 //        }
26772     },
26773
26774     // private
26775     onResize : function(w, h)
26776     {
26777         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
26778         var ew = false;
26779         var eh = false;
26780         
26781         if(this.el ){
26782             if(typeof w == 'number'){
26783                 var aw = w - this.wrap.getFrameWidth('lr');
26784                 this.el.setWidth(this.adjustWidth('textarea', aw));
26785                 ew = aw;
26786             }
26787             if(typeof h == 'number'){
26788                 var tbh = 0;
26789                 for (var i =0; i < this.toolbars.length;i++) {
26790                     // fixme - ask toolbars for heights?
26791                     tbh += this.toolbars[i].tb.el.getHeight();
26792                     if (this.toolbars[i].footer) {
26793                         tbh += this.toolbars[i].footer.el.getHeight();
26794                     }
26795                 }
26796                 
26797                 
26798                 
26799                 
26800                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26801                 ah -= 5; // knock a few pixes off for look..
26802 //                Roo.log(ah);
26803                 this.el.setHeight(this.adjustWidth('textarea', ah));
26804                 var eh = ah;
26805             }
26806         }
26807         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26808         this.editorcore.onResize(ew,eh);
26809         
26810     },
26811
26812     /**
26813      * Toggles the editor between standard and source edit mode.
26814      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26815      */
26816     toggleSourceEdit : function(sourceEditMode)
26817     {
26818         this.editorcore.toggleSourceEdit(sourceEditMode);
26819         
26820         if(this.editorcore.sourceEditMode){
26821             Roo.log('editor - showing textarea');
26822             
26823 //            Roo.log('in');
26824 //            Roo.log(this.syncValue());
26825             this.editorcore.syncValue();
26826             this.el.removeClass('x-hidden');
26827             this.el.dom.removeAttribute('tabIndex');
26828             this.el.focus();
26829             
26830             for (var i = 0; i < this.toolbars.length; i++) {
26831                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
26832                     this.toolbars[i].tb.hide();
26833                     this.toolbars[i].footer.hide();
26834                 }
26835             }
26836             
26837         }else{
26838             Roo.log('editor - hiding textarea');
26839 //            Roo.log('out')
26840 //            Roo.log(this.pushValue()); 
26841             this.editorcore.pushValue();
26842             
26843             this.el.addClass('x-hidden');
26844             this.el.dom.setAttribute('tabIndex', -1);
26845             
26846             for (var i = 0; i < this.toolbars.length; i++) {
26847                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
26848                     this.toolbars[i].tb.show();
26849                     this.toolbars[i].footer.show();
26850                 }
26851             }
26852             
26853             //this.deferFocus();
26854         }
26855         
26856         this.setSize(this.wrap.getSize());
26857         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
26858         
26859         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26860     },
26861  
26862     // private (for BoxComponent)
26863     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26864
26865     // private (for BoxComponent)
26866     getResizeEl : function(){
26867         return this.wrap;
26868     },
26869
26870     // private (for BoxComponent)
26871     getPositionEl : function(){
26872         return this.wrap;
26873     },
26874
26875     // private
26876     initEvents : function(){
26877         this.originalValue = this.getValue();
26878     },
26879
26880     /**
26881      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26882      * @method
26883      */
26884     markInvalid : Roo.emptyFn,
26885     /**
26886      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26887      * @method
26888      */
26889     clearInvalid : Roo.emptyFn,
26890
26891     setValue : function(v){
26892         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
26893         this.editorcore.pushValue();
26894     },
26895
26896      
26897     // private
26898     deferFocus : function(){
26899         this.focus.defer(10, this);
26900     },
26901
26902     // doc'ed in Field
26903     focus : function(){
26904         this.editorcore.focus();
26905         
26906     },
26907       
26908
26909     // private
26910     onDestroy : function(){
26911         
26912         
26913         
26914         if(this.rendered){
26915             
26916             for (var i =0; i < this.toolbars.length;i++) {
26917                 // fixme - ask toolbars for heights?
26918                 this.toolbars[i].onDestroy();
26919             }
26920             
26921             this.wrap.dom.innerHTML = '';
26922             this.wrap.remove();
26923         }
26924     },
26925
26926     // private
26927     onFirstFocus : function(){
26928         //Roo.log("onFirstFocus");
26929         this.editorcore.onFirstFocus();
26930          for (var i =0; i < this.toolbars.length;i++) {
26931             this.toolbars[i].onFirstFocus();
26932         }
26933         
26934     },
26935     
26936     // private
26937     syncValue : function()
26938     {
26939         this.editorcore.syncValue();
26940     },
26941     
26942     pushValue : function()
26943     {
26944         this.editorcore.pushValue();
26945     },
26946     
26947     setStylesheets : function(stylesheets)
26948     {
26949         this.editorcore.setStylesheets(stylesheets);
26950     },
26951     
26952     removeStylesheets : function()
26953     {
26954         this.editorcore.removeStylesheets();
26955     }
26956      
26957     
26958     // hide stuff that is not compatible
26959     /**
26960      * @event blur
26961      * @hide
26962      */
26963     /**
26964      * @event change
26965      * @hide
26966      */
26967     /**
26968      * @event focus
26969      * @hide
26970      */
26971     /**
26972      * @event specialkey
26973      * @hide
26974      */
26975     /**
26976      * @cfg {String} fieldClass @hide
26977      */
26978     /**
26979      * @cfg {String} focusClass @hide
26980      */
26981     /**
26982      * @cfg {String} autoCreate @hide
26983      */
26984     /**
26985      * @cfg {String} inputType @hide
26986      */
26987     /**
26988      * @cfg {String} invalidClass @hide
26989      */
26990     /**
26991      * @cfg {String} invalidText @hide
26992      */
26993     /**
26994      * @cfg {String} msgFx @hide
26995      */
26996     /**
26997      * @cfg {String} validateOnBlur @hide
26998      */
26999 });
27000  
27001     // <script type="text/javascript">
27002 /*
27003  * Based on
27004  * Ext JS Library 1.1.1
27005  * Copyright(c) 2006-2007, Ext JS, LLC.
27006  *  
27007  
27008  */
27009
27010 /**
27011  * @class Roo.form.HtmlEditorToolbar1
27012  * Basic Toolbar
27013  * 
27014  * Usage:
27015  *
27016  new Roo.form.HtmlEditor({
27017     ....
27018     toolbars : [
27019         new Roo.form.HtmlEditorToolbar1({
27020             disable : { fonts: 1 , format: 1, ..., ... , ...],
27021             btns : [ .... ]
27022         })
27023     }
27024      
27025  * 
27026  * @cfg {Object} disable List of elements to disable..
27027  * @cfg {Array} btns List of additional buttons.
27028  * 
27029  * 
27030  * NEEDS Extra CSS? 
27031  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27032  */
27033  
27034 Roo.form.HtmlEditor.ToolbarStandard = function(config)
27035 {
27036     
27037     Roo.apply(this, config);
27038     
27039     // default disabled, based on 'good practice'..
27040     this.disable = this.disable || {};
27041     Roo.applyIf(this.disable, {
27042         fontSize : true,
27043         colors : true,
27044         specialElements : true
27045     });
27046     
27047     
27048     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27049     // dont call parent... till later.
27050 }
27051
27052 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
27053     
27054     tb: false,
27055     
27056     rendered: false,
27057     
27058     editor : false,
27059     editorcore : false,
27060     /**
27061      * @cfg {Object} disable  List of toolbar elements to disable
27062          
27063      */
27064     disable : false,
27065     
27066     
27067      /**
27068      * @cfg {String} createLinkText The default text for the create link prompt
27069      */
27070     createLinkText : 'Please enter the URL for the link:',
27071     /**
27072      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
27073      */
27074     defaultLinkValue : 'http:/'+'/',
27075    
27076     
27077       /**
27078      * @cfg {Array} fontFamilies An array of available font families
27079      */
27080     fontFamilies : [
27081         'Arial',
27082         'Courier New',
27083         'Tahoma',
27084         'Times New Roman',
27085         'Verdana'
27086     ],
27087     
27088     specialChars : [
27089            "&#169;",
27090           "&#174;",     
27091           "&#8482;",    
27092           "&#163;" ,    
27093          // "&#8212;",    
27094           "&#8230;",    
27095           "&#247;" ,    
27096         //  "&#225;" ,     ?? a acute?
27097            "&#8364;"    , //Euro
27098        //   "&#8220;"    ,
27099         //  "&#8221;"    ,
27100         //  "&#8226;"    ,
27101           "&#176;"  //   , // degrees
27102
27103          // "&#233;"     , // e ecute
27104          // "&#250;"     , // u ecute?
27105     ],
27106     
27107     specialElements : [
27108         {
27109             text: "Insert Table",
27110             xtype: 'MenuItem',
27111             xns : Roo.Menu,
27112             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
27113                 
27114         },
27115         {    
27116             text: "Insert Image",
27117             xtype: 'MenuItem',
27118             xns : Roo.Menu,
27119             ihtml : '<img src="about:blank"/>'
27120             
27121         }
27122         
27123          
27124     ],
27125     
27126     
27127     inputElements : [ 
27128             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
27129             "input:submit", "input:button", "select", "textarea", "label" ],
27130     formats : [
27131         ["p"] ,  
27132         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
27133         ["pre"],[ "code"], 
27134         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
27135         ['div'],['span']
27136     ],
27137     
27138     cleanStyles : [
27139         "font-size"
27140     ],
27141      /**
27142      * @cfg {String} defaultFont default font to use.
27143      */
27144     defaultFont: 'tahoma',
27145    
27146     fontSelect : false,
27147     
27148     
27149     formatCombo : false,
27150     
27151     init : function(editor)
27152     {
27153         this.editor = editor;
27154         this.editorcore = editor.editorcore ? editor.editorcore : editor;
27155         var editorcore = this.editorcore;
27156         
27157         var _t = this;
27158         
27159         var fid = editorcore.frameId;
27160         var etb = this;
27161         function btn(id, toggle, handler){
27162             var xid = fid + '-'+ id ;
27163             return {
27164                 id : xid,
27165                 cmd : id,
27166                 cls : 'x-btn-icon x-edit-'+id,
27167                 enableToggle:toggle !== false,
27168                 scope: _t, // was editor...
27169                 handler:handler||_t.relayBtnCmd,
27170                 clickEvent:'mousedown',
27171                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27172                 tabIndex:-1
27173             };
27174         }
27175         
27176         
27177         
27178         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27179         this.tb = tb;
27180          // stop form submits
27181         tb.el.on('click', function(e){
27182             e.preventDefault(); // what does this do?
27183         });
27184
27185         if(!this.disable.font) { // && !Roo.isSafari){
27186             /* why no safari for fonts 
27187             editor.fontSelect = tb.el.createChild({
27188                 tag:'select',
27189                 tabIndex: -1,
27190                 cls:'x-font-select',
27191                 html: this.createFontOptions()
27192             });
27193             
27194             editor.fontSelect.on('change', function(){
27195                 var font = editor.fontSelect.dom.value;
27196                 editor.relayCmd('fontname', font);
27197                 editor.deferFocus();
27198             }, editor);
27199             
27200             tb.add(
27201                 editor.fontSelect.dom,
27202                 '-'
27203             );
27204             */
27205             
27206         };
27207         if(!this.disable.formats){
27208             this.formatCombo = new Roo.form.ComboBox({
27209                 store: new Roo.data.SimpleStore({
27210                     id : 'tag',
27211                     fields: ['tag'],
27212                     data : this.formats // from states.js
27213                 }),
27214                 blockFocus : true,
27215                 name : '',
27216                 //autoCreate : {tag: "div",  size: "20"},
27217                 displayField:'tag',
27218                 typeAhead: false,
27219                 mode: 'local',
27220                 editable : false,
27221                 triggerAction: 'all',
27222                 emptyText:'Add tag',
27223                 selectOnFocus:true,
27224                 width:135,
27225                 listeners : {
27226                     'select': function(c, r, i) {
27227                         editorcore.insertTag(r.get('tag'));
27228                         editor.focus();
27229                     }
27230                 }
27231
27232             });
27233             tb.addField(this.formatCombo);
27234             
27235         }
27236         
27237         if(!this.disable.format){
27238             tb.add(
27239                 btn('bold'),
27240                 btn('italic'),
27241                 btn('underline'),
27242                 btn('strikethrough')
27243             );
27244         };
27245         if(!this.disable.fontSize){
27246             tb.add(
27247                 '-',
27248                 
27249                 
27250                 btn('increasefontsize', false, editorcore.adjustFont),
27251                 btn('decreasefontsize', false, editorcore.adjustFont)
27252             );
27253         };
27254         
27255         
27256         if(!this.disable.colors){
27257             tb.add(
27258                 '-', {
27259                     id:editorcore.frameId +'-forecolor',
27260                     cls:'x-btn-icon x-edit-forecolor',
27261                     clickEvent:'mousedown',
27262                     tooltip: this.buttonTips['forecolor'] || undefined,
27263                     tabIndex:-1,
27264                     menu : new Roo.menu.ColorMenu({
27265                         allowReselect: true,
27266                         focus: Roo.emptyFn,
27267                         value:'000000',
27268                         plain:true,
27269                         selectHandler: function(cp, color){
27270                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
27271                             editor.deferFocus();
27272                         },
27273                         scope: editorcore,
27274                         clickEvent:'mousedown'
27275                     })
27276                 }, {
27277                     id:editorcore.frameId +'backcolor',
27278                     cls:'x-btn-icon x-edit-backcolor',
27279                     clickEvent:'mousedown',
27280                     tooltip: this.buttonTips['backcolor'] || undefined,
27281                     tabIndex:-1,
27282                     menu : new Roo.menu.ColorMenu({
27283                         focus: Roo.emptyFn,
27284                         value:'FFFFFF',
27285                         plain:true,
27286                         allowReselect: true,
27287                         selectHandler: function(cp, color){
27288                             if(Roo.isGecko){
27289                                 editorcore.execCmd('useCSS', false);
27290                                 editorcore.execCmd('hilitecolor', color);
27291                                 editorcore.execCmd('useCSS', true);
27292                                 editor.deferFocus();
27293                             }else{
27294                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
27295                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
27296                                 editor.deferFocus();
27297                             }
27298                         },
27299                         scope:editorcore,
27300                         clickEvent:'mousedown'
27301                     })
27302                 }
27303             );
27304         };
27305         // now add all the items...
27306         
27307
27308         if(!this.disable.alignments){
27309             tb.add(
27310                 '-',
27311                 btn('justifyleft'),
27312                 btn('justifycenter'),
27313                 btn('justifyright')
27314             );
27315         };
27316
27317         //if(!Roo.isSafari){
27318             if(!this.disable.links){
27319                 tb.add(
27320                     '-',
27321                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
27322                 );
27323             };
27324
27325             if(!this.disable.lists){
27326                 tb.add(
27327                     '-',
27328                     btn('insertorderedlist'),
27329                     btn('insertunorderedlist')
27330                 );
27331             }
27332             if(!this.disable.sourceEdit){
27333                 tb.add(
27334                     '-',
27335                     btn('sourceedit', true, function(btn){
27336                         this.toggleSourceEdit(btn.pressed);
27337                     })
27338                 );
27339             }
27340         //}
27341         
27342         var smenu = { };
27343         // special menu.. - needs to be tidied up..
27344         if (!this.disable.special) {
27345             smenu = {
27346                 text: "&#169;",
27347                 cls: 'x-edit-none',
27348                 
27349                 menu : {
27350                     items : []
27351                 }
27352             };
27353             for (var i =0; i < this.specialChars.length; i++) {
27354                 smenu.menu.items.push({
27355                     
27356                     html: this.specialChars[i],
27357                     handler: function(a,b) {
27358                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
27359                         //editor.insertAtCursor(a.html);
27360                         
27361                     },
27362                     tabIndex:-1
27363                 });
27364             }
27365             
27366             
27367             tb.add(smenu);
27368             
27369             
27370         }
27371         
27372         var cmenu = { };
27373         if (!this.disable.cleanStyles) {
27374             cmenu = {
27375                 cls: 'x-btn-icon x-btn-clear',
27376                 
27377                 menu : {
27378                     items : []
27379                 }
27380             };
27381             for (var i =0; i < this.cleanStyles.length; i++) {
27382                 cmenu.menu.items.push({
27383                     actiontype : this.cleanStyles[i],
27384                     html: 'Remove ' + this.cleanStyles[i],
27385                     handler: function(a,b) {
27386 //                        Roo.log(a);
27387 //                        Roo.log(b);
27388                         var c = Roo.get(editorcore.doc.body);
27389                         c.select('[style]').each(function(s) {
27390                             s.dom.style.removeProperty(a.actiontype);
27391                         });
27392                         editorcore.syncValue();
27393                     },
27394                     tabIndex:-1
27395                 });
27396             }
27397              cmenu.menu.items.push({
27398                 actiontype : 'tablewidths',
27399                 html: 'Remove Table Widths',
27400                 handler: function(a,b) {
27401                     editorcore.cleanTableWidths();
27402                     editorcore.syncValue();
27403                 },
27404                 tabIndex:-1
27405             });
27406             cmenu.menu.items.push({
27407                 actiontype : 'word',
27408                 html: 'Remove MS Word Formating',
27409                 handler: function(a,b) {
27410                     editorcore.cleanWord();
27411                     editorcore.syncValue();
27412                 },
27413                 tabIndex:-1
27414             });
27415             
27416             cmenu.menu.items.push({
27417                 actiontype : 'all',
27418                 html: 'Remove All Styles',
27419                 handler: function(a,b) {
27420                     
27421                     var c = Roo.get(editorcore.doc.body);
27422                     c.select('[style]').each(function(s) {
27423                         s.dom.removeAttribute('style');
27424                     });
27425                     editorcore.syncValue();
27426                 },
27427                 tabIndex:-1
27428             });
27429             
27430             cmenu.menu.items.push({
27431                 actiontype : 'all',
27432                 html: 'Remove All CSS Classes',
27433                 handler: function(a,b) {
27434                     
27435                     var c = Roo.get(editorcore.doc.body);
27436                     c.select('[class]').each(function(s) {
27437                         s.dom.className = '';
27438                     });
27439                     editorcore.syncValue();
27440                 },
27441                 tabIndex:-1
27442             });
27443             
27444              cmenu.menu.items.push({
27445                 actiontype : 'tidy',
27446                 html: 'Tidy HTML Source',
27447                 handler: function(a,b) {
27448                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
27449                     editorcore.syncValue();
27450                 },
27451                 tabIndex:-1
27452             });
27453             
27454             
27455             tb.add(cmenu);
27456         }
27457          
27458         if (!this.disable.specialElements) {
27459             var semenu = {
27460                 text: "Other;",
27461                 cls: 'x-edit-none',
27462                 menu : {
27463                     items : []
27464                 }
27465             };
27466             for (var i =0; i < this.specialElements.length; i++) {
27467                 semenu.menu.items.push(
27468                     Roo.apply({ 
27469                         handler: function(a,b) {
27470                             editor.insertAtCursor(this.ihtml);
27471                         }
27472                     }, this.specialElements[i])
27473                 );
27474                     
27475             }
27476             
27477             tb.add(semenu);
27478             
27479             
27480         }
27481          
27482         
27483         if (this.btns) {
27484             for(var i =0; i< this.btns.length;i++) {
27485                 var b = Roo.factory(this.btns[i],Roo.form);
27486                 b.cls =  'x-edit-none';
27487                 
27488                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
27489                     b.cls += ' x-init-enable';
27490                 }
27491                 
27492                 b.scope = editorcore;
27493                 tb.add(b);
27494             }
27495         
27496         }
27497         
27498         
27499         
27500         // disable everything...
27501         
27502         this.tb.items.each(function(item){
27503             
27504            if(
27505                 item.id != editorcore.frameId+ '-sourceedit' && 
27506                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
27507             ){
27508                 
27509                 item.disable();
27510             }
27511         });
27512         this.rendered = true;
27513         
27514         // the all the btns;
27515         editor.on('editorevent', this.updateToolbar, this);
27516         // other toolbars need to implement this..
27517         //editor.on('editmodechange', this.updateToolbar, this);
27518     },
27519     
27520     
27521     relayBtnCmd : function(btn) {
27522         this.editorcore.relayCmd(btn.cmd);
27523     },
27524     // private used internally
27525     createLink : function(){
27526         Roo.log("create link?");
27527         var url = prompt(this.createLinkText, this.defaultLinkValue);
27528         if(url && url != 'http:/'+'/'){
27529             this.editorcore.relayCmd('createlink', url);
27530         }
27531     },
27532
27533     
27534     /**
27535      * Protected method that will not generally be called directly. It triggers
27536      * a toolbar update by reading the markup state of the current selection in the editor.
27537      */
27538     updateToolbar: function(){
27539
27540         if(!this.editorcore.activated){
27541             this.editor.onFirstFocus();
27542             return;
27543         }
27544
27545         var btns = this.tb.items.map, 
27546             doc = this.editorcore.doc,
27547             frameId = this.editorcore.frameId;
27548
27549         if(!this.disable.font && !Roo.isSafari){
27550             /*
27551             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
27552             if(name != this.fontSelect.dom.value){
27553                 this.fontSelect.dom.value = name;
27554             }
27555             */
27556         }
27557         if(!this.disable.format){
27558             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
27559             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
27560             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
27561             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
27562         }
27563         if(!this.disable.alignments){
27564             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
27565             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
27566             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
27567         }
27568         if(!Roo.isSafari && !this.disable.lists){
27569             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
27570             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
27571         }
27572         
27573         var ans = this.editorcore.getAllAncestors();
27574         if (this.formatCombo) {
27575             
27576             
27577             var store = this.formatCombo.store;
27578             this.formatCombo.setValue("");
27579             for (var i =0; i < ans.length;i++) {
27580                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27581                     // select it..
27582                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27583                     break;
27584                 }
27585             }
27586         }
27587         
27588         
27589         
27590         // hides menus... - so this cant be on a menu...
27591         Roo.menu.MenuMgr.hideAll();
27592
27593         //this.editorsyncValue();
27594     },
27595    
27596     
27597     createFontOptions : function(){
27598         var buf = [], fs = this.fontFamilies, ff, lc;
27599         
27600         
27601         
27602         for(var i = 0, len = fs.length; i< len; i++){
27603             ff = fs[i];
27604             lc = ff.toLowerCase();
27605             buf.push(
27606                 '<option value="',lc,'" style="font-family:',ff,';"',
27607                     (this.defaultFont == lc ? ' selected="true">' : '>'),
27608                     ff,
27609                 '</option>'
27610             );
27611         }
27612         return buf.join('');
27613     },
27614     
27615     toggleSourceEdit : function(sourceEditMode){
27616         
27617         Roo.log("toolbar toogle");
27618         if(sourceEditMode === undefined){
27619             sourceEditMode = !this.sourceEditMode;
27620         }
27621         this.sourceEditMode = sourceEditMode === true;
27622         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
27623         // just toggle the button?
27624         if(btn.pressed !== this.sourceEditMode){
27625             btn.toggle(this.sourceEditMode);
27626             return;
27627         }
27628         
27629         if(sourceEditMode){
27630             Roo.log("disabling buttons");
27631             this.tb.items.each(function(item){
27632                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
27633                     item.disable();
27634                 }
27635             });
27636           
27637         }else{
27638             Roo.log("enabling buttons");
27639             if(this.editorcore.initialized){
27640                 this.tb.items.each(function(item){
27641                     item.enable();
27642                 });
27643             }
27644             
27645         }
27646         Roo.log("calling toggole on editor");
27647         // tell the editor that it's been pressed..
27648         this.editor.toggleSourceEdit(sourceEditMode);
27649        
27650     },
27651      /**
27652      * Object collection of toolbar tooltips for the buttons in the editor. The key
27653      * is the command id associated with that button and the value is a valid QuickTips object.
27654      * For example:
27655 <pre><code>
27656 {
27657     bold : {
27658         title: 'Bold (Ctrl+B)',
27659         text: 'Make the selected text bold.',
27660         cls: 'x-html-editor-tip'
27661     },
27662     italic : {
27663         title: 'Italic (Ctrl+I)',
27664         text: 'Make the selected text italic.',
27665         cls: 'x-html-editor-tip'
27666     },
27667     ...
27668 </code></pre>
27669     * @type Object
27670      */
27671     buttonTips : {
27672         bold : {
27673             title: 'Bold (Ctrl+B)',
27674             text: 'Make the selected text bold.',
27675             cls: 'x-html-editor-tip'
27676         },
27677         italic : {
27678             title: 'Italic (Ctrl+I)',
27679             text: 'Make the selected text italic.',
27680             cls: 'x-html-editor-tip'
27681         },
27682         underline : {
27683             title: 'Underline (Ctrl+U)',
27684             text: 'Underline the selected text.',
27685             cls: 'x-html-editor-tip'
27686         },
27687         strikethrough : {
27688             title: 'Strikethrough',
27689             text: 'Strikethrough the selected text.',
27690             cls: 'x-html-editor-tip'
27691         },
27692         increasefontsize : {
27693             title: 'Grow Text',
27694             text: 'Increase the font size.',
27695             cls: 'x-html-editor-tip'
27696         },
27697         decreasefontsize : {
27698             title: 'Shrink Text',
27699             text: 'Decrease the font size.',
27700             cls: 'x-html-editor-tip'
27701         },
27702         backcolor : {
27703             title: 'Text Highlight Color',
27704             text: 'Change the background color of the selected text.',
27705             cls: 'x-html-editor-tip'
27706         },
27707         forecolor : {
27708             title: 'Font Color',
27709             text: 'Change the color of the selected text.',
27710             cls: 'x-html-editor-tip'
27711         },
27712         justifyleft : {
27713             title: 'Align Text Left',
27714             text: 'Align text to the left.',
27715             cls: 'x-html-editor-tip'
27716         },
27717         justifycenter : {
27718             title: 'Center Text',
27719             text: 'Center text in the editor.',
27720             cls: 'x-html-editor-tip'
27721         },
27722         justifyright : {
27723             title: 'Align Text Right',
27724             text: 'Align text to the right.',
27725             cls: 'x-html-editor-tip'
27726         },
27727         insertunorderedlist : {
27728             title: 'Bullet List',
27729             text: 'Start a bulleted list.',
27730             cls: 'x-html-editor-tip'
27731         },
27732         insertorderedlist : {
27733             title: 'Numbered List',
27734             text: 'Start a numbered list.',
27735             cls: 'x-html-editor-tip'
27736         },
27737         createlink : {
27738             title: 'Hyperlink',
27739             text: 'Make the selected text a hyperlink.',
27740             cls: 'x-html-editor-tip'
27741         },
27742         sourceedit : {
27743             title: 'Source Edit',
27744             text: 'Switch to source editing mode.',
27745             cls: 'x-html-editor-tip'
27746         }
27747     },
27748     // private
27749     onDestroy : function(){
27750         if(this.rendered){
27751             
27752             this.tb.items.each(function(item){
27753                 if(item.menu){
27754                     item.menu.removeAll();
27755                     if(item.menu.el){
27756                         item.menu.el.destroy();
27757                     }
27758                 }
27759                 item.destroy();
27760             });
27761              
27762         }
27763     },
27764     onFirstFocus: function() {
27765         this.tb.items.each(function(item){
27766            item.enable();
27767         });
27768     }
27769 });
27770
27771
27772
27773
27774 // <script type="text/javascript">
27775 /*
27776  * Based on
27777  * Ext JS Library 1.1.1
27778  * Copyright(c) 2006-2007, Ext JS, LLC.
27779  *  
27780  
27781  */
27782
27783  
27784 /**
27785  * @class Roo.form.HtmlEditor.ToolbarContext
27786  * Context Toolbar
27787  * 
27788  * Usage:
27789  *
27790  new Roo.form.HtmlEditor({
27791     ....
27792     toolbars : [
27793         { xtype: 'ToolbarStandard', styles : {} }
27794         { xtype: 'ToolbarContext', disable : {} }
27795     ]
27796 })
27797
27798      
27799  * 
27800  * @config : {Object} disable List of elements to disable.. (not done yet.)
27801  * @config : {Object} styles  Map of styles available.
27802  * 
27803  */
27804
27805 Roo.form.HtmlEditor.ToolbarContext = function(config)
27806 {
27807     
27808     Roo.apply(this, config);
27809     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27810     // dont call parent... till later.
27811     this.styles = this.styles || {};
27812 }
27813
27814  
27815
27816 Roo.form.HtmlEditor.ToolbarContext.types = {
27817     'IMG' : {
27818         width : {
27819             title: "Width",
27820             width: 40
27821         },
27822         height:  {
27823             title: "Height",
27824             width: 40
27825         },
27826         align: {
27827             title: "Align",
27828             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
27829             width : 80
27830             
27831         },
27832         border: {
27833             title: "Border",
27834             width: 40
27835         },
27836         alt: {
27837             title: "Alt",
27838             width: 120
27839         },
27840         src : {
27841             title: "Src",
27842             width: 220
27843         }
27844         
27845     },
27846     'A' : {
27847         name : {
27848             title: "Name",
27849             width: 50
27850         },
27851         target:  {
27852             title: "Target",
27853             width: 120
27854         },
27855         href:  {
27856             title: "Href",
27857             width: 220
27858         } // border?
27859         
27860     },
27861     'TABLE' : {
27862         rows : {
27863             title: "Rows",
27864             width: 20
27865         },
27866         cols : {
27867             title: "Cols",
27868             width: 20
27869         },
27870         width : {
27871             title: "Width",
27872             width: 40
27873         },
27874         height : {
27875             title: "Height",
27876             width: 40
27877         },
27878         border : {
27879             title: "Border",
27880             width: 20
27881         }
27882     },
27883     'TD' : {
27884         width : {
27885             title: "Width",
27886             width: 40
27887         },
27888         height : {
27889             title: "Height",
27890             width: 40
27891         },   
27892         align: {
27893             title: "Align",
27894             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27895             width: 80
27896         },
27897         valign: {
27898             title: "Valign",
27899             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27900             width: 80
27901         },
27902         colspan: {
27903             title: "Colspan",
27904             width: 20
27905             
27906         },
27907          'font-family'  : {
27908             title : "Font",
27909             style : 'fontFamily',
27910             displayField: 'display',
27911             optname : 'font-family',
27912             width: 140
27913         }
27914     },
27915     'INPUT' : {
27916         name : {
27917             title: "name",
27918             width: 120
27919         },
27920         value : {
27921             title: "Value",
27922             width: 120
27923         },
27924         width : {
27925             title: "Width",
27926             width: 40
27927         }
27928     },
27929     'LABEL' : {
27930         'for' : {
27931             title: "For",
27932             width: 120
27933         }
27934     },
27935     'TEXTAREA' : {
27936           name : {
27937             title: "name",
27938             width: 120
27939         },
27940         rows : {
27941             title: "Rows",
27942             width: 20
27943         },
27944         cols : {
27945             title: "Cols",
27946             width: 20
27947         }
27948     },
27949     'SELECT' : {
27950         name : {
27951             title: "name",
27952             width: 120
27953         },
27954         selectoptions : {
27955             title: "Options",
27956             width: 200
27957         }
27958     },
27959     
27960     // should we really allow this??
27961     // should this just be 
27962     'BODY' : {
27963         title : {
27964             title: "Title",
27965             width: 200,
27966             disabled : true
27967         }
27968     },
27969     'SPAN' : {
27970         'font-family'  : {
27971             title : "Font",
27972             style : 'fontFamily',
27973             displayField: 'display',
27974             optname : 'font-family',
27975             width: 140
27976         }
27977     },
27978     'DIV' : {
27979         'font-family'  : {
27980             title : "Font",
27981             style : 'fontFamily',
27982             displayField: 'display',
27983             optname : 'font-family',
27984             width: 140
27985         }
27986     },
27987      'P' : {
27988         'font-family'  : {
27989             title : "Font",
27990             style : 'fontFamily',
27991             displayField: 'display',
27992             optname : 'font-family',
27993             width: 140
27994         }
27995     },
27996     
27997     '*' : {
27998         // empty..
27999     }
28000
28001 };
28002
28003 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
28004 Roo.form.HtmlEditor.ToolbarContext.stores = false;
28005
28006 Roo.form.HtmlEditor.ToolbarContext.options = {
28007         'font-family'  : [ 
28008                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
28009                 [ 'Courier New', 'Courier New'],
28010                 [ 'Tahoma', 'Tahoma'],
28011                 [ 'Times New Roman,serif', 'Times'],
28012                 [ 'Verdana','Verdana' ]
28013         ]
28014 };
28015
28016 // fixme - these need to be configurable..
28017  
28018
28019 //Roo.form.HtmlEditor.ToolbarContext.types
28020
28021
28022 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
28023     
28024     tb: false,
28025     
28026     rendered: false,
28027     
28028     editor : false,
28029     editorcore : false,
28030     /**
28031      * @cfg {Object} disable  List of toolbar elements to disable
28032          
28033      */
28034     disable : false,
28035     /**
28036      * @cfg {Object} styles List of styles 
28037      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
28038      *
28039      * These must be defined in the page, so they get rendered correctly..
28040      * .headline { }
28041      * TD.underline { }
28042      * 
28043      */
28044     styles : false,
28045     
28046     options: false,
28047     
28048     toolbars : false,
28049     
28050     init : function(editor)
28051     {
28052         this.editor = editor;
28053         this.editorcore = editor.editorcore ? editor.editorcore : editor;
28054         var editorcore = this.editorcore;
28055         
28056         var fid = editorcore.frameId;
28057         var etb = this;
28058         function btn(id, toggle, handler){
28059             var xid = fid + '-'+ id ;
28060             return {
28061                 id : xid,
28062                 cmd : id,
28063                 cls : 'x-btn-icon x-edit-'+id,
28064                 enableToggle:toggle !== false,
28065                 scope: editorcore, // was editor...
28066                 handler:handler||editorcore.relayBtnCmd,
28067                 clickEvent:'mousedown',
28068                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
28069                 tabIndex:-1
28070             };
28071         }
28072         // create a new element.
28073         var wdiv = editor.wrap.createChild({
28074                 tag: 'div'
28075             }, editor.wrap.dom.firstChild.nextSibling, true);
28076         
28077         // can we do this more than once??
28078         
28079          // stop form submits
28080       
28081  
28082         // disable everything...
28083         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
28084         this.toolbars = {};
28085            
28086         for (var i in  ty) {
28087           
28088             this.toolbars[i] = this.buildToolbar(ty[i],i);
28089         }
28090         this.tb = this.toolbars.BODY;
28091         this.tb.el.show();
28092         this.buildFooter();
28093         this.footer.show();
28094         editor.on('hide', function( ) { this.footer.hide() }, this);
28095         editor.on('show', function( ) { this.footer.show() }, this);
28096         
28097          
28098         this.rendered = true;
28099         
28100         // the all the btns;
28101         editor.on('editorevent', this.updateToolbar, this);
28102         // other toolbars need to implement this..
28103         //editor.on('editmodechange', this.updateToolbar, this);
28104     },
28105     
28106     
28107     
28108     /**
28109      * Protected method that will not generally be called directly. It triggers
28110      * a toolbar update by reading the markup state of the current selection in the editor.
28111      *
28112      * Note you can force an update by calling on('editorevent', scope, false)
28113      */
28114     updateToolbar: function(editor,ev,sel){
28115
28116         //Roo.log(ev);
28117         // capture mouse up - this is handy for selecting images..
28118         // perhaps should go somewhere else...
28119         if(!this.editorcore.activated){
28120              this.editor.onFirstFocus();
28121             return;
28122         }
28123         
28124         
28125         
28126         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
28127         // selectNode - might want to handle IE?
28128         if (ev &&
28129             (ev.type == 'mouseup' || ev.type == 'click' ) &&
28130             ev.target && ev.target.tagName == 'IMG') {
28131             // they have click on an image...
28132             // let's see if we can change the selection...
28133             sel = ev.target;
28134          
28135               var nodeRange = sel.ownerDocument.createRange();
28136             try {
28137                 nodeRange.selectNode(sel);
28138             } catch (e) {
28139                 nodeRange.selectNodeContents(sel);
28140             }
28141             //nodeRange.collapse(true);
28142             var s = this.editorcore.win.getSelection();
28143             s.removeAllRanges();
28144             s.addRange(nodeRange);
28145         }  
28146         
28147       
28148         var updateFooter = sel ? false : true;
28149         
28150         
28151         var ans = this.editorcore.getAllAncestors();
28152         
28153         // pick
28154         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
28155         
28156         if (!sel) { 
28157             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
28158             sel = sel ? sel : this.editorcore.doc.body;
28159             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
28160             
28161         }
28162         // pick a menu that exists..
28163         var tn = sel.tagName.toUpperCase();
28164         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
28165         
28166         tn = sel.tagName.toUpperCase();
28167         
28168         var lastSel = this.tb.selectedNode;
28169         
28170         this.tb.selectedNode = sel;
28171         
28172         // if current menu does not match..
28173         
28174         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
28175                 
28176             this.tb.el.hide();
28177             ///console.log("show: " + tn);
28178             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
28179             this.tb.el.show();
28180             // update name
28181             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
28182             
28183             
28184             // update attributes
28185             if (this.tb.fields) {
28186                 this.tb.fields.each(function(e) {
28187                     if (e.stylename) {
28188                         e.setValue(sel.style[e.stylename]);
28189                         return;
28190                     } 
28191                    e.setValue(sel.getAttribute(e.attrname));
28192                 });
28193             }
28194             
28195             var hasStyles = false;
28196             for(var i in this.styles) {
28197                 hasStyles = true;
28198                 break;
28199             }
28200             
28201             // update styles
28202             if (hasStyles) { 
28203                 var st = this.tb.fields.item(0);
28204                 
28205                 st.store.removeAll();
28206                
28207                 
28208                 var cn = sel.className.split(/\s+/);
28209                 
28210                 var avs = [];
28211                 if (this.styles['*']) {
28212                     
28213                     Roo.each(this.styles['*'], function(v) {
28214                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
28215                     });
28216                 }
28217                 if (this.styles[tn]) { 
28218                     Roo.each(this.styles[tn], function(v) {
28219                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
28220                     });
28221                 }
28222                 
28223                 st.store.loadData(avs);
28224                 st.collapse();
28225                 st.setValue(cn);
28226             }
28227             // flag our selected Node.
28228             this.tb.selectedNode = sel;
28229            
28230            
28231             Roo.menu.MenuMgr.hideAll();
28232
28233         }
28234         
28235         if (!updateFooter) {
28236             //this.footDisp.dom.innerHTML = ''; 
28237             return;
28238         }
28239         // update the footer
28240         //
28241         var html = '';
28242         
28243         this.footerEls = ans.reverse();
28244         Roo.each(this.footerEls, function(a,i) {
28245             if (!a) { return; }
28246             html += html.length ? ' &gt; '  :  '';
28247             
28248             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
28249             
28250         });
28251        
28252         // 
28253         var sz = this.footDisp.up('td').getSize();
28254         this.footDisp.dom.style.width = (sz.width -10) + 'px';
28255         this.footDisp.dom.style.marginLeft = '5px';
28256         
28257         this.footDisp.dom.style.overflow = 'hidden';
28258         
28259         this.footDisp.dom.innerHTML = html;
28260             
28261         //this.editorsyncValue();
28262     },
28263      
28264     
28265    
28266        
28267     // private
28268     onDestroy : function(){
28269         if(this.rendered){
28270             
28271             this.tb.items.each(function(item){
28272                 if(item.menu){
28273                     item.menu.removeAll();
28274                     if(item.menu.el){
28275                         item.menu.el.destroy();
28276                     }
28277                 }
28278                 item.destroy();
28279             });
28280              
28281         }
28282     },
28283     onFirstFocus: function() {
28284         // need to do this for all the toolbars..
28285         this.tb.items.each(function(item){
28286            item.enable();
28287         });
28288     },
28289     buildToolbar: function(tlist, nm)
28290     {
28291         var editor = this.editor;
28292         var editorcore = this.editorcore;
28293          // create a new element.
28294         var wdiv = editor.wrap.createChild({
28295                 tag: 'div'
28296             }, editor.wrap.dom.firstChild.nextSibling, true);
28297         
28298        
28299         var tb = new Roo.Toolbar(wdiv);
28300         // add the name..
28301         
28302         tb.add(nm+ ":&nbsp;");
28303         
28304         var styles = [];
28305         for(var i in this.styles) {
28306             styles.push(i);
28307         }
28308         
28309         // styles...
28310         if (styles && styles.length) {
28311             
28312             // this needs a multi-select checkbox...
28313             tb.addField( new Roo.form.ComboBox({
28314                 store: new Roo.data.SimpleStore({
28315                     id : 'val',
28316                     fields: ['val', 'selected'],
28317                     data : [] 
28318                 }),
28319                 name : '-roo-edit-className',
28320                 attrname : 'className',
28321                 displayField: 'val',
28322                 typeAhead: false,
28323                 mode: 'local',
28324                 editable : false,
28325                 triggerAction: 'all',
28326                 emptyText:'Select Style',
28327                 selectOnFocus:true,
28328                 width: 130,
28329                 listeners : {
28330                     'select': function(c, r, i) {
28331                         // initial support only for on class per el..
28332                         tb.selectedNode.className =  r ? r.get('val') : '';
28333                         editorcore.syncValue();
28334                     }
28335                 }
28336     
28337             }));
28338         }
28339         
28340         var tbc = Roo.form.HtmlEditor.ToolbarContext;
28341         var tbops = tbc.options;
28342         
28343         for (var i in tlist) {
28344             
28345             var item = tlist[i];
28346             tb.add(item.title + ":&nbsp;");
28347             
28348             
28349             //optname == used so you can configure the options available..
28350             var opts = item.opts ? item.opts : false;
28351             if (item.optname) {
28352                 opts = tbops[item.optname];
28353            
28354             }
28355             
28356             if (opts) {
28357                 // opts == pulldown..
28358                 tb.addField( new Roo.form.ComboBox({
28359                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
28360                         id : 'val',
28361                         fields: ['val', 'display'],
28362                         data : opts  
28363                     }),
28364                     name : '-roo-edit-' + i,
28365                     attrname : i,
28366                     stylename : item.style ? item.style : false,
28367                     displayField: item.displayField ? item.displayField : 'val',
28368                     valueField :  'val',
28369                     typeAhead: false,
28370                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
28371                     editable : false,
28372                     triggerAction: 'all',
28373                     emptyText:'Select',
28374                     selectOnFocus:true,
28375                     width: item.width ? item.width  : 130,
28376                     listeners : {
28377                         'select': function(c, r, i) {
28378                             if (c.stylename) {
28379                                 tb.selectedNode.style[c.stylename] =  r.get('val');
28380                                 return;
28381                             }
28382                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
28383                         }
28384                     }
28385
28386                 }));
28387                 continue;
28388                     
28389                  
28390                 
28391                 tb.addField( new Roo.form.TextField({
28392                     name: i,
28393                     width: 100,
28394                     //allowBlank:false,
28395                     value: ''
28396                 }));
28397                 continue;
28398             }
28399             tb.addField( new Roo.form.TextField({
28400                 name: '-roo-edit-' + i,
28401                 attrname : i,
28402                 
28403                 width: item.width,
28404                 //allowBlank:true,
28405                 value: '',
28406                 listeners: {
28407                     'change' : function(f, nv, ov) {
28408                         tb.selectedNode.setAttribute(f.attrname, nv);
28409                     }
28410                 }
28411             }));
28412              
28413         }
28414         
28415         var _this = this;
28416         
28417         if(nm == 'BODY'){
28418             tb.addSeparator();
28419         
28420             tb.addButton( {
28421                 text: 'Stylesheets',
28422
28423                 listeners : {
28424                     click : function ()
28425                     {
28426                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
28427                     }
28428                 }
28429             });
28430         }
28431         
28432         tb.addFill();
28433         tb.addButton( {
28434             text: 'Remove Tag',
28435     
28436             listeners : {
28437                 click : function ()
28438                 {
28439                     // remove
28440                     // undo does not work.
28441                      
28442                     var sn = tb.selectedNode;
28443                     
28444                     var pn = sn.parentNode;
28445                     
28446                     var stn =  sn.childNodes[0];
28447                     var en = sn.childNodes[sn.childNodes.length - 1 ];
28448                     while (sn.childNodes.length) {
28449                         var node = sn.childNodes[0];
28450                         sn.removeChild(node);
28451                         //Roo.log(node);
28452                         pn.insertBefore(node, sn);
28453                         
28454                     }
28455                     pn.removeChild(sn);
28456                     var range = editorcore.createRange();
28457         
28458                     range.setStart(stn,0);
28459                     range.setEnd(en,0); //????
28460                     //range.selectNode(sel);
28461                     
28462                     
28463                     var selection = editorcore.getSelection();
28464                     selection.removeAllRanges();
28465                     selection.addRange(range);
28466                     
28467                     
28468                     
28469                     //_this.updateToolbar(null, null, pn);
28470                     _this.updateToolbar(null, null, null);
28471                     _this.footDisp.dom.innerHTML = ''; 
28472                 }
28473             }
28474             
28475                     
28476                 
28477             
28478         });
28479         
28480         
28481         tb.el.on('click', function(e){
28482             e.preventDefault(); // what does this do?
28483         });
28484         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
28485         tb.el.hide();
28486         tb.name = nm;
28487         // dont need to disable them... as they will get hidden
28488         return tb;
28489          
28490         
28491     },
28492     buildFooter : function()
28493     {
28494         
28495         var fel = this.editor.wrap.createChild();
28496         this.footer = new Roo.Toolbar(fel);
28497         // toolbar has scrolly on left / right?
28498         var footDisp= new Roo.Toolbar.Fill();
28499         var _t = this;
28500         this.footer.add(
28501             {
28502                 text : '&lt;',
28503                 xtype: 'Button',
28504                 handler : function() {
28505                     _t.footDisp.scrollTo('left',0,true)
28506                 }
28507             }
28508         );
28509         this.footer.add( footDisp );
28510         this.footer.add( 
28511             {
28512                 text : '&gt;',
28513                 xtype: 'Button',
28514                 handler : function() {
28515                     // no animation..
28516                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
28517                 }
28518             }
28519         );
28520         var fel = Roo.get(footDisp.el);
28521         fel.addClass('x-editor-context');
28522         this.footDispWrap = fel; 
28523         this.footDispWrap.overflow  = 'hidden';
28524         
28525         this.footDisp = fel.createChild();
28526         this.footDispWrap.on('click', this.onContextClick, this)
28527         
28528         
28529     },
28530     onContextClick : function (ev,dom)
28531     {
28532         ev.preventDefault();
28533         var  cn = dom.className;
28534         //Roo.log(cn);
28535         if (!cn.match(/x-ed-loc-/)) {
28536             return;
28537         }
28538         var n = cn.split('-').pop();
28539         var ans = this.footerEls;
28540         var sel = ans[n];
28541         
28542          // pick
28543         var range = this.editorcore.createRange();
28544         
28545         range.selectNodeContents(sel);
28546         //range.selectNode(sel);
28547         
28548         
28549         var selection = this.editorcore.getSelection();
28550         selection.removeAllRanges();
28551         selection.addRange(range);
28552         
28553         
28554         
28555         this.updateToolbar(null, null, sel);
28556         
28557         
28558     }
28559     
28560     
28561     
28562     
28563     
28564 });
28565
28566
28567
28568
28569
28570 /*
28571  * Based on:
28572  * Ext JS Library 1.1.1
28573  * Copyright(c) 2006-2007, Ext JS, LLC.
28574  *
28575  * Originally Released Under LGPL - original licence link has changed is not relivant.
28576  *
28577  * Fork - LGPL
28578  * <script type="text/javascript">
28579  */
28580  
28581 /**
28582  * @class Roo.form.BasicForm
28583  * @extends Roo.util.Observable
28584  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
28585  * @constructor
28586  * @param {String/HTMLElement/Roo.Element} el The form element or its id
28587  * @param {Object} config Configuration options
28588  */
28589 Roo.form.BasicForm = function(el, config){
28590     this.allItems = [];
28591     this.childForms = [];
28592     Roo.apply(this, config);
28593     /*
28594      * The Roo.form.Field items in this form.
28595      * @type MixedCollection
28596      */
28597      
28598      
28599     this.items = new Roo.util.MixedCollection(false, function(o){
28600         return o.id || (o.id = Roo.id());
28601     });
28602     this.addEvents({
28603         /**
28604          * @event beforeaction
28605          * Fires before any action is performed. Return false to cancel the action.
28606          * @param {Form} this
28607          * @param {Action} action The action to be performed
28608          */
28609         beforeaction: true,
28610         /**
28611          * @event actionfailed
28612          * Fires when an action fails.
28613          * @param {Form} this
28614          * @param {Action} action The action that failed
28615          */
28616         actionfailed : true,
28617         /**
28618          * @event actioncomplete
28619          * Fires when an action is completed.
28620          * @param {Form} this
28621          * @param {Action} action The action that completed
28622          */
28623         actioncomplete : true
28624     });
28625     if(el){
28626         this.initEl(el);
28627     }
28628     Roo.form.BasicForm.superclass.constructor.call(this);
28629 };
28630
28631 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
28632     /**
28633      * @cfg {String} method
28634      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
28635      */
28636     /**
28637      * @cfg {DataReader} reader
28638      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
28639      * This is optional as there is built-in support for processing JSON.
28640      */
28641     /**
28642      * @cfg {DataReader} errorReader
28643      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
28644      * This is completely optional as there is built-in support for processing JSON.
28645      */
28646     /**
28647      * @cfg {String} url
28648      * The URL to use for form actions if one isn't supplied in the action options.
28649      */
28650     /**
28651      * @cfg {Boolean} fileUpload
28652      * Set to true if this form is a file upload.
28653      */
28654      
28655     /**
28656      * @cfg {Object} baseParams
28657      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
28658      */
28659      /**
28660      
28661     /**
28662      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
28663      */
28664     timeout: 30,
28665
28666     // private
28667     activeAction : null,
28668
28669     /**
28670      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
28671      * or setValues() data instead of when the form was first created.
28672      */
28673     trackResetOnLoad : false,
28674     
28675     
28676     /**
28677      * childForms - used for multi-tab forms
28678      * @type {Array}
28679      */
28680     childForms : false,
28681     
28682     /**
28683      * allItems - full list of fields.
28684      * @type {Array}
28685      */
28686     allItems : false,
28687     
28688     /**
28689      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
28690      * element by passing it or its id or mask the form itself by passing in true.
28691      * @type Mixed
28692      */
28693     waitMsgTarget : false,
28694
28695     // private
28696     initEl : function(el){
28697         this.el = Roo.get(el);
28698         this.id = this.el.id || Roo.id();
28699         this.el.on('submit', this.onSubmit, this);
28700         this.el.addClass('x-form');
28701     },
28702
28703     // private
28704     onSubmit : function(e){
28705         e.stopEvent();
28706     },
28707
28708     /**
28709      * Returns true if client-side validation on the form is successful.
28710      * @return Boolean
28711      */
28712     isValid : function(){
28713         var valid = true;
28714         this.items.each(function(f){
28715            if(!f.validate()){
28716                valid = false;
28717            }
28718         });
28719         return valid;
28720     },
28721
28722     /**
28723      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
28724      * @return Boolean
28725      */
28726     isDirty : function(){
28727         var dirty = false;
28728         this.items.each(function(f){
28729            if(f.isDirty()){
28730                dirty = true;
28731                return false;
28732            }
28733         });
28734         return dirty;
28735     },
28736     
28737     /**
28738      * Returns true if any fields in this form have changed since their original load. (New version)
28739      * @return Boolean
28740      */
28741     
28742     hasChanged : function()
28743     {
28744         var dirty = false;
28745         this.items.each(function(f){
28746            if(f.hasChanged()){
28747                dirty = true;
28748                return false;
28749            }
28750         });
28751         return dirty;
28752         
28753     },
28754     /**
28755      * Resets all hasChanged to 'false' -
28756      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
28757      * So hasChanged storage is only to be used for this purpose
28758      * @return Boolean
28759      */
28760     resetHasChanged : function()
28761     {
28762         this.items.each(function(f){
28763            f.resetHasChanged();
28764         });
28765         
28766     },
28767     
28768     
28769     /**
28770      * Performs a predefined action (submit or load) or custom actions you define on this form.
28771      * @param {String} actionName The name of the action type
28772      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
28773      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
28774      * accept other config options):
28775      * <pre>
28776 Property          Type             Description
28777 ----------------  ---------------  ----------------------------------------------------------------------------------
28778 url               String           The url for the action (defaults to the form's url)
28779 method            String           The form method to use (defaults to the form's method, or POST if not defined)
28780 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
28781 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
28782                                    validate the form on the client (defaults to false)
28783      * </pre>
28784      * @return {BasicForm} this
28785      */
28786     doAction : function(action, options){
28787         if(typeof action == 'string'){
28788             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
28789         }
28790         if(this.fireEvent('beforeaction', this, action) !== false){
28791             this.beforeAction(action);
28792             action.run.defer(100, action);
28793         }
28794         return this;
28795     },
28796
28797     /**
28798      * Shortcut to do a submit action.
28799      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28800      * @return {BasicForm} this
28801      */
28802     submit : function(options){
28803         this.doAction('submit', options);
28804         return this;
28805     },
28806
28807     /**
28808      * Shortcut to do a load action.
28809      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28810      * @return {BasicForm} this
28811      */
28812     load : function(options){
28813         this.doAction('load', options);
28814         return this;
28815     },
28816
28817     /**
28818      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
28819      * @param {Record} record The record to edit
28820      * @return {BasicForm} this
28821      */
28822     updateRecord : function(record){
28823         record.beginEdit();
28824         var fs = record.fields;
28825         fs.each(function(f){
28826             var field = this.findField(f.name);
28827             if(field){
28828                 record.set(f.name, field.getValue());
28829             }
28830         }, this);
28831         record.endEdit();
28832         return this;
28833     },
28834
28835     /**
28836      * Loads an Roo.data.Record into this form.
28837      * @param {Record} record The record to load
28838      * @return {BasicForm} this
28839      */
28840     loadRecord : function(record){
28841         this.setValues(record.data);
28842         return this;
28843     },
28844
28845     // private
28846     beforeAction : function(action){
28847         var o = action.options;
28848         
28849        
28850         if(this.waitMsgTarget === true){
28851             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
28852         }else if(this.waitMsgTarget){
28853             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
28854             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
28855         }else {
28856             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
28857         }
28858          
28859     },
28860
28861     // private
28862     afterAction : function(action, success){
28863         this.activeAction = null;
28864         var o = action.options;
28865         
28866         if(this.waitMsgTarget === true){
28867             this.el.unmask();
28868         }else if(this.waitMsgTarget){
28869             this.waitMsgTarget.unmask();
28870         }else{
28871             Roo.MessageBox.updateProgress(1);
28872             Roo.MessageBox.hide();
28873         }
28874          
28875         if(success){
28876             if(o.reset){
28877                 this.reset();
28878             }
28879             Roo.callback(o.success, o.scope, [this, action]);
28880             this.fireEvent('actioncomplete', this, action);
28881             
28882         }else{
28883             
28884             // failure condition..
28885             // we have a scenario where updates need confirming.
28886             // eg. if a locking scenario exists..
28887             // we look for { errors : { needs_confirm : true }} in the response.
28888             if (
28889                 (typeof(action.result) != 'undefined')  &&
28890                 (typeof(action.result.errors) != 'undefined')  &&
28891                 (typeof(action.result.errors.needs_confirm) != 'undefined')
28892            ){
28893                 var _t = this;
28894                 Roo.MessageBox.confirm(
28895                     "Change requires confirmation",
28896                     action.result.errorMsg,
28897                     function(r) {
28898                         if (r != 'yes') {
28899                             return;
28900                         }
28901                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
28902                     }
28903                     
28904                 );
28905                 
28906                 
28907                 
28908                 return;
28909             }
28910             
28911             Roo.callback(o.failure, o.scope, [this, action]);
28912             // show an error message if no failed handler is set..
28913             if (!this.hasListener('actionfailed')) {
28914                 Roo.MessageBox.alert("Error",
28915                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
28916                         action.result.errorMsg :
28917                         "Saving Failed, please check your entries or try again"
28918                 );
28919             }
28920             
28921             this.fireEvent('actionfailed', this, action);
28922         }
28923         
28924     },
28925
28926     /**
28927      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
28928      * @param {String} id The value to search for
28929      * @return Field
28930      */
28931     findField : function(id){
28932         var field = this.items.get(id);
28933         if(!field){
28934             this.items.each(function(f){
28935                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
28936                     field = f;
28937                     return false;
28938                 }
28939             });
28940         }
28941         return field || null;
28942     },
28943
28944     /**
28945      * Add a secondary form to this one, 
28946      * Used to provide tabbed forms. One form is primary, with hidden values 
28947      * which mirror the elements from the other forms.
28948      * 
28949      * @param {Roo.form.Form} form to add.
28950      * 
28951      */
28952     addForm : function(form)
28953     {
28954        
28955         if (this.childForms.indexOf(form) > -1) {
28956             // already added..
28957             return;
28958         }
28959         this.childForms.push(form);
28960         var n = '';
28961         Roo.each(form.allItems, function (fe) {
28962             
28963             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
28964             if (this.findField(n)) { // already added..
28965                 return;
28966             }
28967             var add = new Roo.form.Hidden({
28968                 name : n
28969             });
28970             add.render(this.el);
28971             
28972             this.add( add );
28973         }, this);
28974         
28975     },
28976     /**
28977      * Mark fields in this form invalid in bulk.
28978      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
28979      * @return {BasicForm} this
28980      */
28981     markInvalid : function(errors){
28982         if(errors instanceof Array){
28983             for(var i = 0, len = errors.length; i < len; i++){
28984                 var fieldError = errors[i];
28985                 var f = this.findField(fieldError.id);
28986                 if(f){
28987                     f.markInvalid(fieldError.msg);
28988                 }
28989             }
28990         }else{
28991             var field, id;
28992             for(id in errors){
28993                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
28994                     field.markInvalid(errors[id]);
28995                 }
28996             }
28997         }
28998         Roo.each(this.childForms || [], function (f) {
28999             f.markInvalid(errors);
29000         });
29001         
29002         return this;
29003     },
29004
29005     /**
29006      * Set values for fields in this form in bulk.
29007      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
29008      * @return {BasicForm} this
29009      */
29010     setValues : function(values){
29011         if(values instanceof Array){ // array of objects
29012             for(var i = 0, len = values.length; i < len; i++){
29013                 var v = values[i];
29014                 var f = this.findField(v.id);
29015                 if(f){
29016                     f.setValue(v.value);
29017                     if(this.trackResetOnLoad){
29018                         f.originalValue = f.getValue();
29019                     }
29020                 }
29021             }
29022         }else{ // object hash
29023             var field, id;
29024             for(id in values){
29025                 if(typeof values[id] != 'function' && (field = this.findField(id))){
29026                     
29027                     if (field.setFromData && 
29028                         field.valueField && 
29029                         field.displayField &&
29030                         // combos' with local stores can 
29031                         // be queried via setValue()
29032                         // to set their value..
29033                         (field.store && !field.store.isLocal)
29034                         ) {
29035                         // it's a combo
29036                         var sd = { };
29037                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
29038                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
29039                         field.setFromData(sd);
29040                         
29041                     } else {
29042                         field.setValue(values[id]);
29043                     }
29044                     
29045                     
29046                     if(this.trackResetOnLoad){
29047                         field.originalValue = field.getValue();
29048                     }
29049                 }
29050             }
29051         }
29052         this.resetHasChanged();
29053         
29054         
29055         Roo.each(this.childForms || [], function (f) {
29056             f.setValues(values);
29057             f.resetHasChanged();
29058         });
29059                 
29060         return this;
29061     },
29062
29063     /**
29064      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
29065      * they are returned as an array.
29066      * @param {Boolean} asString
29067      * @return {Object}
29068      */
29069     getValues : function(asString){
29070         if (this.childForms) {
29071             // copy values from the child forms
29072             Roo.each(this.childForms, function (f) {
29073                 this.setValues(f.getValues());
29074             }, this);
29075         }
29076         
29077         
29078         
29079         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
29080         if(asString === true){
29081             return fs;
29082         }
29083         return Roo.urlDecode(fs);
29084     },
29085     
29086     /**
29087      * Returns the fields in this form as an object with key/value pairs. 
29088      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
29089      * @return {Object}
29090      */
29091     getFieldValues : function(with_hidden)
29092     {
29093         if (this.childForms) {
29094             // copy values from the child forms
29095             // should this call getFieldValues - probably not as we do not currently copy
29096             // hidden fields when we generate..
29097             Roo.each(this.childForms, function (f) {
29098                 this.setValues(f.getValues());
29099             }, this);
29100         }
29101         
29102         var ret = {};
29103         this.items.each(function(f){
29104             if (!f.getName()) {
29105                 return;
29106             }
29107             var v = f.getValue();
29108             if (f.inputType =='radio') {
29109                 if (typeof(ret[f.getName()]) == 'undefined') {
29110                     ret[f.getName()] = ''; // empty..
29111                 }
29112                 
29113                 if (!f.el.dom.checked) {
29114                     return;
29115                     
29116                 }
29117                 v = f.el.dom.value;
29118                 
29119             }
29120             
29121             // not sure if this supported any more..
29122             if ((typeof(v) == 'object') && f.getRawValue) {
29123                 v = f.getRawValue() ; // dates..
29124             }
29125             // combo boxes where name != hiddenName...
29126             if (f.name != f.getName()) {
29127                 ret[f.name] = f.getRawValue();
29128             }
29129             ret[f.getName()] = v;
29130         });
29131         
29132         return ret;
29133     },
29134
29135     /**
29136      * Clears all invalid messages in this form.
29137      * @return {BasicForm} this
29138      */
29139     clearInvalid : function(){
29140         this.items.each(function(f){
29141            f.clearInvalid();
29142         });
29143         
29144         Roo.each(this.childForms || [], function (f) {
29145             f.clearInvalid();
29146         });
29147         
29148         
29149         return this;
29150     },
29151
29152     /**
29153      * Resets this form.
29154      * @return {BasicForm} this
29155      */
29156     reset : function(){
29157         this.items.each(function(f){
29158             f.reset();
29159         });
29160         
29161         Roo.each(this.childForms || [], function (f) {
29162             f.reset();
29163         });
29164         this.resetHasChanged();
29165         
29166         return this;
29167     },
29168
29169     /**
29170      * Add Roo.form components to this form.
29171      * @param {Field} field1
29172      * @param {Field} field2 (optional)
29173      * @param {Field} etc (optional)
29174      * @return {BasicForm} this
29175      */
29176     add : function(){
29177         this.items.addAll(Array.prototype.slice.call(arguments, 0));
29178         return this;
29179     },
29180
29181
29182     /**
29183      * Removes a field from the items collection (does NOT remove its markup).
29184      * @param {Field} field
29185      * @return {BasicForm} this
29186      */
29187     remove : function(field){
29188         this.items.remove(field);
29189         return this;
29190     },
29191
29192     /**
29193      * Looks at the fields in this form, checks them for an id attribute,
29194      * and calls applyTo on the existing dom element with that id.
29195      * @return {BasicForm} this
29196      */
29197     render : function(){
29198         this.items.each(function(f){
29199             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
29200                 f.applyTo(f.id);
29201             }
29202         });
29203         return this;
29204     },
29205
29206     /**
29207      * Calls {@link Ext#apply} for all fields in this form with the passed object.
29208      * @param {Object} values
29209      * @return {BasicForm} this
29210      */
29211     applyToFields : function(o){
29212         this.items.each(function(f){
29213            Roo.apply(f, o);
29214         });
29215         return this;
29216     },
29217
29218     /**
29219      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
29220      * @param {Object} values
29221      * @return {BasicForm} this
29222      */
29223     applyIfToFields : function(o){
29224         this.items.each(function(f){
29225            Roo.applyIf(f, o);
29226         });
29227         return this;
29228     }
29229 });
29230
29231 // back compat
29232 Roo.BasicForm = Roo.form.BasicForm;/*
29233  * Based on:
29234  * Ext JS Library 1.1.1
29235  * Copyright(c) 2006-2007, Ext JS, LLC.
29236  *
29237  * Originally Released Under LGPL - original licence link has changed is not relivant.
29238  *
29239  * Fork - LGPL
29240  * <script type="text/javascript">
29241  */
29242
29243 /**
29244  * @class Roo.form.Form
29245  * @extends Roo.form.BasicForm
29246  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
29247  * @constructor
29248  * @param {Object} config Configuration options
29249  */
29250 Roo.form.Form = function(config){
29251     var xitems =  [];
29252     if (config.items) {
29253         xitems = config.items;
29254         delete config.items;
29255     }
29256    
29257     
29258     Roo.form.Form.superclass.constructor.call(this, null, config);
29259     this.url = this.url || this.action;
29260     if(!this.root){
29261         this.root = new Roo.form.Layout(Roo.applyIf({
29262             id: Roo.id()
29263         }, config));
29264     }
29265     this.active = this.root;
29266     /**
29267      * Array of all the buttons that have been added to this form via {@link addButton}
29268      * @type Array
29269      */
29270     this.buttons = [];
29271     this.allItems = [];
29272     this.addEvents({
29273         /**
29274          * @event clientvalidation
29275          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
29276          * @param {Form} this
29277          * @param {Boolean} valid true if the form has passed client-side validation
29278          */
29279         clientvalidation: true,
29280         /**
29281          * @event rendered
29282          * Fires when the form is rendered
29283          * @param {Roo.form.Form} form
29284          */
29285         rendered : true
29286     });
29287     
29288     if (this.progressUrl) {
29289             // push a hidden field onto the list of fields..
29290             this.addxtype( {
29291                     xns: Roo.form, 
29292                     xtype : 'Hidden', 
29293                     name : 'UPLOAD_IDENTIFIER' 
29294             });
29295         }
29296         
29297     
29298     Roo.each(xitems, this.addxtype, this);
29299     
29300     
29301     
29302 };
29303
29304 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
29305     /**
29306      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
29307      */
29308     /**
29309      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
29310      */
29311     /**
29312      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
29313      */
29314     buttonAlign:'center',
29315
29316     /**
29317      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
29318      */
29319     minButtonWidth:75,
29320
29321     /**
29322      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
29323      * This property cascades to child containers if not set.
29324      */
29325     labelAlign:'left',
29326
29327     /**
29328      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
29329      * fires a looping event with that state. This is required to bind buttons to the valid
29330      * state using the config value formBind:true on the button.
29331      */
29332     monitorValid : false,
29333
29334     /**
29335      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
29336      */
29337     monitorPoll : 200,
29338     
29339     /**
29340      * @cfg {String} progressUrl - Url to return progress data 
29341      */
29342     
29343     progressUrl : false,
29344   
29345     /**
29346      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
29347      * fields are added and the column is closed. If no fields are passed the column remains open
29348      * until end() is called.
29349      * @param {Object} config The config to pass to the column
29350      * @param {Field} field1 (optional)
29351      * @param {Field} field2 (optional)
29352      * @param {Field} etc (optional)
29353      * @return Column The column container object
29354      */
29355     column : function(c){
29356         var col = new Roo.form.Column(c);
29357         this.start(col);
29358         if(arguments.length > 1){ // duplicate code required because of Opera
29359             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29360             this.end();
29361         }
29362         return col;
29363     },
29364
29365     /**
29366      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
29367      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
29368      * until end() is called.
29369      * @param {Object} config The config to pass to the fieldset
29370      * @param {Field} field1 (optional)
29371      * @param {Field} field2 (optional)
29372      * @param {Field} etc (optional)
29373      * @return FieldSet The fieldset container object
29374      */
29375     fieldset : function(c){
29376         var fs = new Roo.form.FieldSet(c);
29377         this.start(fs);
29378         if(arguments.length > 1){ // duplicate code required because of Opera
29379             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29380             this.end();
29381         }
29382         return fs;
29383     },
29384
29385     /**
29386      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
29387      * fields are added and the container is closed. If no fields are passed the container remains open
29388      * until end() is called.
29389      * @param {Object} config The config to pass to the Layout
29390      * @param {Field} field1 (optional)
29391      * @param {Field} field2 (optional)
29392      * @param {Field} etc (optional)
29393      * @return Layout The container object
29394      */
29395     container : function(c){
29396         var l = new Roo.form.Layout(c);
29397         this.start(l);
29398         if(arguments.length > 1){ // duplicate code required because of Opera
29399             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29400             this.end();
29401         }
29402         return l;
29403     },
29404
29405     /**
29406      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
29407      * @param {Object} container A Roo.form.Layout or subclass of Layout
29408      * @return {Form} this
29409      */
29410     start : function(c){
29411         // cascade label info
29412         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
29413         this.active.stack.push(c);
29414         c.ownerCt = this.active;
29415         this.active = c;
29416         return this;
29417     },
29418
29419     /**
29420      * Closes the current open container
29421      * @return {Form} this
29422      */
29423     end : function(){
29424         if(this.active == this.root){
29425             return this;
29426         }
29427         this.active = this.active.ownerCt;
29428         return this;
29429     },
29430
29431     /**
29432      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
29433      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
29434      * as the label of the field.
29435      * @param {Field} field1
29436      * @param {Field} field2 (optional)
29437      * @param {Field} etc. (optional)
29438      * @return {Form} this
29439      */
29440     add : function(){
29441         this.active.stack.push.apply(this.active.stack, arguments);
29442         this.allItems.push.apply(this.allItems,arguments);
29443         var r = [];
29444         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
29445             if(a[i].isFormField){
29446                 r.push(a[i]);
29447             }
29448         }
29449         if(r.length > 0){
29450             Roo.form.Form.superclass.add.apply(this, r);
29451         }
29452         return this;
29453     },
29454     
29455
29456     
29457     
29458     
29459      /**
29460      * Find any element that has been added to a form, using it's ID or name
29461      * This can include framesets, columns etc. along with regular fields..
29462      * @param {String} id - id or name to find.
29463      
29464      * @return {Element} e - or false if nothing found.
29465      */
29466     findbyId : function(id)
29467     {
29468         var ret = false;
29469         if (!id) {
29470             return ret;
29471         }
29472         Roo.each(this.allItems, function(f){
29473             if (f.id == id || f.name == id ){
29474                 ret = f;
29475                 return false;
29476             }
29477         });
29478         return ret;
29479     },
29480
29481     
29482     
29483     /**
29484      * Render this form into the passed container. This should only be called once!
29485      * @param {String/HTMLElement/Element} container The element this component should be rendered into
29486      * @return {Form} this
29487      */
29488     render : function(ct)
29489     {
29490         
29491         
29492         
29493         ct = Roo.get(ct);
29494         var o = this.autoCreate || {
29495             tag: 'form',
29496             method : this.method || 'POST',
29497             id : this.id || Roo.id()
29498         };
29499         this.initEl(ct.createChild(o));
29500
29501         this.root.render(this.el);
29502         
29503        
29504              
29505         this.items.each(function(f){
29506             f.render('x-form-el-'+f.id);
29507         });
29508
29509         if(this.buttons.length > 0){
29510             // tables are required to maintain order and for correct IE layout
29511             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
29512                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
29513                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29514             }}, null, true);
29515             var tr = tb.getElementsByTagName('tr')[0];
29516             for(var i = 0, len = this.buttons.length; i < len; i++) {
29517                 var b = this.buttons[i];
29518                 var td = document.createElement('td');
29519                 td.className = 'x-form-btn-td';
29520                 b.render(tr.appendChild(td));
29521             }
29522         }
29523         if(this.monitorValid){ // initialize after render
29524             this.startMonitoring();
29525         }
29526         this.fireEvent('rendered', this);
29527         return this;
29528     },
29529
29530     /**
29531      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
29532      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29533      * object or a valid Roo.DomHelper element config
29534      * @param {Function} handler The function called when the button is clicked
29535      * @param {Object} scope (optional) The scope of the handler function
29536      * @return {Roo.Button}
29537      */
29538     addButton : function(config, handler, scope){
29539         var bc = {
29540             handler: handler,
29541             scope: scope,
29542             minWidth: this.minButtonWidth,
29543             hideParent:true
29544         };
29545         if(typeof config == "string"){
29546             bc.text = config;
29547         }else{
29548             Roo.apply(bc, config);
29549         }
29550         var btn = new Roo.Button(null, bc);
29551         this.buttons.push(btn);
29552         return btn;
29553     },
29554
29555      /**
29556      * Adds a series of form elements (using the xtype property as the factory method.
29557      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
29558      * @param {Object} config 
29559      */
29560     
29561     addxtype : function()
29562     {
29563         var ar = Array.prototype.slice.call(arguments, 0);
29564         var ret = false;
29565         for(var i = 0; i < ar.length; i++) {
29566             if (!ar[i]) {
29567                 continue; // skip -- if this happends something invalid got sent, we 
29568                 // should ignore it, as basically that interface element will not show up
29569                 // and that should be pretty obvious!!
29570             }
29571             
29572             if (Roo.form[ar[i].xtype]) {
29573                 ar[i].form = this;
29574                 var fe = Roo.factory(ar[i], Roo.form);
29575                 if (!ret) {
29576                     ret = fe;
29577                 }
29578                 fe.form = this;
29579                 if (fe.store) {
29580                     fe.store.form = this;
29581                 }
29582                 if (fe.isLayout) {  
29583                          
29584                     this.start(fe);
29585                     this.allItems.push(fe);
29586                     if (fe.items && fe.addxtype) {
29587                         fe.addxtype.apply(fe, fe.items);
29588                         delete fe.items;
29589                     }
29590                      this.end();
29591                     continue;
29592                 }
29593                 
29594                 
29595                  
29596                 this.add(fe);
29597               //  console.log('adding ' + ar[i].xtype);
29598             }
29599             if (ar[i].xtype == 'Button') {  
29600                 //console.log('adding button');
29601                 //console.log(ar[i]);
29602                 this.addButton(ar[i]);
29603                 this.allItems.push(fe);
29604                 continue;
29605             }
29606             
29607             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
29608                 alert('end is not supported on xtype any more, use items');
29609             //    this.end();
29610             //    //console.log('adding end');
29611             }
29612             
29613         }
29614         return ret;
29615     },
29616     
29617     /**
29618      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
29619      * option "monitorValid"
29620      */
29621     startMonitoring : function(){
29622         if(!this.bound){
29623             this.bound = true;
29624             Roo.TaskMgr.start({
29625                 run : this.bindHandler,
29626                 interval : this.monitorPoll || 200,
29627                 scope: this
29628             });
29629         }
29630     },
29631
29632     /**
29633      * Stops monitoring of the valid state of this form
29634      */
29635     stopMonitoring : function(){
29636         this.bound = false;
29637     },
29638
29639     // private
29640     bindHandler : function(){
29641         if(!this.bound){
29642             return false; // stops binding
29643         }
29644         var valid = true;
29645         this.items.each(function(f){
29646             if(!f.isValid(true)){
29647                 valid = false;
29648                 return false;
29649             }
29650         });
29651         for(var i = 0, len = this.buttons.length; i < len; i++){
29652             var btn = this.buttons[i];
29653             if(btn.formBind === true && btn.disabled === valid){
29654                 btn.setDisabled(!valid);
29655             }
29656         }
29657         this.fireEvent('clientvalidation', this, valid);
29658     }
29659     
29660     
29661     
29662     
29663     
29664     
29665     
29666     
29667 });
29668
29669
29670 // back compat
29671 Roo.Form = Roo.form.Form;
29672 /*
29673  * Based on:
29674  * Ext JS Library 1.1.1
29675  * Copyright(c) 2006-2007, Ext JS, LLC.
29676  *
29677  * Originally Released Under LGPL - original licence link has changed is not relivant.
29678  *
29679  * Fork - LGPL
29680  * <script type="text/javascript">
29681  */
29682
29683 // as we use this in bootstrap.
29684 Roo.namespace('Roo.form');
29685  /**
29686  * @class Roo.form.Action
29687  * Internal Class used to handle form actions
29688  * @constructor
29689  * @param {Roo.form.BasicForm} el The form element or its id
29690  * @param {Object} config Configuration options
29691  */
29692
29693  
29694  
29695 // define the action interface
29696 Roo.form.Action = function(form, options){
29697     this.form = form;
29698     this.options = options || {};
29699 };
29700 /**
29701  * Client Validation Failed
29702  * @const 
29703  */
29704 Roo.form.Action.CLIENT_INVALID = 'client';
29705 /**
29706  * Server Validation Failed
29707  * @const 
29708  */
29709 Roo.form.Action.SERVER_INVALID = 'server';
29710  /**
29711  * Connect to Server Failed
29712  * @const 
29713  */
29714 Roo.form.Action.CONNECT_FAILURE = 'connect';
29715 /**
29716  * Reading Data from Server Failed
29717  * @const 
29718  */
29719 Roo.form.Action.LOAD_FAILURE = 'load';
29720
29721 Roo.form.Action.prototype = {
29722     type : 'default',
29723     failureType : undefined,
29724     response : undefined,
29725     result : undefined,
29726
29727     // interface method
29728     run : function(options){
29729
29730     },
29731
29732     // interface method
29733     success : function(response){
29734
29735     },
29736
29737     // interface method
29738     handleResponse : function(response){
29739
29740     },
29741
29742     // default connection failure
29743     failure : function(response){
29744         
29745         this.response = response;
29746         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29747         this.form.afterAction(this, false);
29748     },
29749
29750     processResponse : function(response){
29751         this.response = response;
29752         if(!response.responseText){
29753             return true;
29754         }
29755         this.result = this.handleResponse(response);
29756         return this.result;
29757     },
29758
29759     // utility functions used internally
29760     getUrl : function(appendParams){
29761         var url = this.options.url || this.form.url || this.form.el.dom.action;
29762         if(appendParams){
29763             var p = this.getParams();
29764             if(p){
29765                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
29766             }
29767         }
29768         return url;
29769     },
29770
29771     getMethod : function(){
29772         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
29773     },
29774
29775     getParams : function(){
29776         var bp = this.form.baseParams;
29777         var p = this.options.params;
29778         if(p){
29779             if(typeof p == "object"){
29780                 p = Roo.urlEncode(Roo.applyIf(p, bp));
29781             }else if(typeof p == 'string' && bp){
29782                 p += '&' + Roo.urlEncode(bp);
29783             }
29784         }else if(bp){
29785             p = Roo.urlEncode(bp);
29786         }
29787         return p;
29788     },
29789
29790     createCallback : function(){
29791         return {
29792             success: this.success,
29793             failure: this.failure,
29794             scope: this,
29795             timeout: (this.form.timeout*1000),
29796             upload: this.form.fileUpload ? this.success : undefined
29797         };
29798     }
29799 };
29800
29801 Roo.form.Action.Submit = function(form, options){
29802     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
29803 };
29804
29805 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
29806     type : 'submit',
29807
29808     haveProgress : false,
29809     uploadComplete : false,
29810     
29811     // uploadProgress indicator.
29812     uploadProgress : function()
29813     {
29814         if (!this.form.progressUrl) {
29815             return;
29816         }
29817         
29818         if (!this.haveProgress) {
29819             Roo.MessageBox.progress("Uploading", "Uploading");
29820         }
29821         if (this.uploadComplete) {
29822            Roo.MessageBox.hide();
29823            return;
29824         }
29825         
29826         this.haveProgress = true;
29827    
29828         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
29829         
29830         var c = new Roo.data.Connection();
29831         c.request({
29832             url : this.form.progressUrl,
29833             params: {
29834                 id : uid
29835             },
29836             method: 'GET',
29837             success : function(req){
29838                //console.log(data);
29839                 var rdata = false;
29840                 var edata;
29841                 try  {
29842                    rdata = Roo.decode(req.responseText)
29843                 } catch (e) {
29844                     Roo.log("Invalid data from server..");
29845                     Roo.log(edata);
29846                     return;
29847                 }
29848                 if (!rdata || !rdata.success) {
29849                     Roo.log(rdata);
29850                     Roo.MessageBox.alert(Roo.encode(rdata));
29851                     return;
29852                 }
29853                 var data = rdata.data;
29854                 
29855                 if (this.uploadComplete) {
29856                    Roo.MessageBox.hide();
29857                    return;
29858                 }
29859                    
29860                 if (data){
29861                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
29862                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
29863                     );
29864                 }
29865                 this.uploadProgress.defer(2000,this);
29866             },
29867        
29868             failure: function(data) {
29869                 Roo.log('progress url failed ');
29870                 Roo.log(data);
29871             },
29872             scope : this
29873         });
29874            
29875     },
29876     
29877     
29878     run : function()
29879     {
29880         // run get Values on the form, so it syncs any secondary forms.
29881         this.form.getValues();
29882         
29883         var o = this.options;
29884         var method = this.getMethod();
29885         var isPost = method == 'POST';
29886         if(o.clientValidation === false || this.form.isValid()){
29887             
29888             if (this.form.progressUrl) {
29889                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
29890                     (new Date() * 1) + '' + Math.random());
29891                     
29892             } 
29893             
29894             
29895             Roo.Ajax.request(Roo.apply(this.createCallback(), {
29896                 form:this.form.el.dom,
29897                 url:this.getUrl(!isPost),
29898                 method: method,
29899                 params:isPost ? this.getParams() : null,
29900                 isUpload: this.form.fileUpload
29901             }));
29902             
29903             this.uploadProgress();
29904
29905         }else if (o.clientValidation !== false){ // client validation failed
29906             this.failureType = Roo.form.Action.CLIENT_INVALID;
29907             this.form.afterAction(this, false);
29908         }
29909     },
29910
29911     success : function(response)
29912     {
29913         this.uploadComplete= true;
29914         if (this.haveProgress) {
29915             Roo.MessageBox.hide();
29916         }
29917         
29918         
29919         var result = this.processResponse(response);
29920         if(result === true || result.success){
29921             this.form.afterAction(this, true);
29922             return;
29923         }
29924         if(result.errors){
29925             this.form.markInvalid(result.errors);
29926             this.failureType = Roo.form.Action.SERVER_INVALID;
29927         }
29928         this.form.afterAction(this, false);
29929     },
29930     failure : function(response)
29931     {
29932         this.uploadComplete= true;
29933         if (this.haveProgress) {
29934             Roo.MessageBox.hide();
29935         }
29936         
29937         this.response = response;
29938         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29939         this.form.afterAction(this, false);
29940     },
29941     
29942     handleResponse : function(response){
29943         if(this.form.errorReader){
29944             var rs = this.form.errorReader.read(response);
29945             var errors = [];
29946             if(rs.records){
29947                 for(var i = 0, len = rs.records.length; i < len; i++) {
29948                     var r = rs.records[i];
29949                     errors[i] = r.data;
29950                 }
29951             }
29952             if(errors.length < 1){
29953                 errors = null;
29954             }
29955             return {
29956                 success : rs.success,
29957                 errors : errors
29958             };
29959         }
29960         var ret = false;
29961         try {
29962             ret = Roo.decode(response.responseText);
29963         } catch (e) {
29964             ret = {
29965                 success: false,
29966                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
29967                 errors : []
29968             };
29969         }
29970         return ret;
29971         
29972     }
29973 });
29974
29975
29976 Roo.form.Action.Load = function(form, options){
29977     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
29978     this.reader = this.form.reader;
29979 };
29980
29981 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
29982     type : 'load',
29983
29984     run : function(){
29985         
29986         Roo.Ajax.request(Roo.apply(
29987                 this.createCallback(), {
29988                     method:this.getMethod(),
29989                     url:this.getUrl(false),
29990                     params:this.getParams()
29991         }));
29992     },
29993
29994     success : function(response){
29995         
29996         var result = this.processResponse(response);
29997         if(result === true || !result.success || !result.data){
29998             this.failureType = Roo.form.Action.LOAD_FAILURE;
29999             this.form.afterAction(this, false);
30000             return;
30001         }
30002         this.form.clearInvalid();
30003         this.form.setValues(result.data);
30004         this.form.afterAction(this, true);
30005     },
30006
30007     handleResponse : function(response){
30008         if(this.form.reader){
30009             var rs = this.form.reader.read(response);
30010             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
30011             return {
30012                 success : rs.success,
30013                 data : data
30014             };
30015         }
30016         return Roo.decode(response.responseText);
30017     }
30018 });
30019
30020 Roo.form.Action.ACTION_TYPES = {
30021     'load' : Roo.form.Action.Load,
30022     'submit' : Roo.form.Action.Submit
30023 };/*
30024  * Based on:
30025  * Ext JS Library 1.1.1
30026  * Copyright(c) 2006-2007, Ext JS, LLC.
30027  *
30028  * Originally Released Under LGPL - original licence link has changed is not relivant.
30029  *
30030  * Fork - LGPL
30031  * <script type="text/javascript">
30032  */
30033  
30034 /**
30035  * @class Roo.form.Layout
30036  * @extends Roo.Component
30037  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
30038  * @constructor
30039  * @param {Object} config Configuration options
30040  */
30041 Roo.form.Layout = function(config){
30042     var xitems = [];
30043     if (config.items) {
30044         xitems = config.items;
30045         delete config.items;
30046     }
30047     Roo.form.Layout.superclass.constructor.call(this, config);
30048     this.stack = [];
30049     Roo.each(xitems, this.addxtype, this);
30050      
30051 };
30052
30053 Roo.extend(Roo.form.Layout, Roo.Component, {
30054     /**
30055      * @cfg {String/Object} autoCreate
30056      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
30057      */
30058     /**
30059      * @cfg {String/Object/Function} style
30060      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
30061      * a function which returns such a specification.
30062      */
30063     /**
30064      * @cfg {String} labelAlign
30065      * Valid values are "left," "top" and "right" (defaults to "left")
30066      */
30067     /**
30068      * @cfg {Number} labelWidth
30069      * Fixed width in pixels of all field labels (defaults to undefined)
30070      */
30071     /**
30072      * @cfg {Boolean} clear
30073      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
30074      */
30075     clear : true,
30076     /**
30077      * @cfg {String} labelSeparator
30078      * The separator to use after field labels (defaults to ':')
30079      */
30080     labelSeparator : ':',
30081     /**
30082      * @cfg {Boolean} hideLabels
30083      * True to suppress the display of field labels in this layout (defaults to false)
30084      */
30085     hideLabels : false,
30086
30087     // private
30088     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
30089     
30090     isLayout : true,
30091     
30092     // private
30093     onRender : function(ct, position){
30094         if(this.el){ // from markup
30095             this.el = Roo.get(this.el);
30096         }else {  // generate
30097             var cfg = this.getAutoCreate();
30098             this.el = ct.createChild(cfg, position);
30099         }
30100         if(this.style){
30101             this.el.applyStyles(this.style);
30102         }
30103         if(this.labelAlign){
30104             this.el.addClass('x-form-label-'+this.labelAlign);
30105         }
30106         if(this.hideLabels){
30107             this.labelStyle = "display:none";
30108             this.elementStyle = "padding-left:0;";
30109         }else{
30110             if(typeof this.labelWidth == 'number'){
30111                 this.labelStyle = "width:"+this.labelWidth+"px;";
30112                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
30113             }
30114             if(this.labelAlign == 'top'){
30115                 this.labelStyle = "width:auto;";
30116                 this.elementStyle = "padding-left:0;";
30117             }
30118         }
30119         var stack = this.stack;
30120         var slen = stack.length;
30121         if(slen > 0){
30122             if(!this.fieldTpl){
30123                 var t = new Roo.Template(
30124                     '<div class="x-form-item {5}">',
30125                         '<label for="{0}" style="{2}">{1}{4}</label>',
30126                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30127                         '</div>',
30128                     '</div><div class="x-form-clear-left"></div>'
30129                 );
30130                 t.disableFormats = true;
30131                 t.compile();
30132                 Roo.form.Layout.prototype.fieldTpl = t;
30133             }
30134             for(var i = 0; i < slen; i++) {
30135                 if(stack[i].isFormField){
30136                     this.renderField(stack[i]);
30137                 }else{
30138                     this.renderComponent(stack[i]);
30139                 }
30140             }
30141         }
30142         if(this.clear){
30143             this.el.createChild({cls:'x-form-clear'});
30144         }
30145     },
30146
30147     // private
30148     renderField : function(f){
30149         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
30150                f.id, //0
30151                f.fieldLabel, //1
30152                f.labelStyle||this.labelStyle||'', //2
30153                this.elementStyle||'', //3
30154                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
30155                f.itemCls||this.itemCls||''  //5
30156        ], true).getPrevSibling());
30157     },
30158
30159     // private
30160     renderComponent : function(c){
30161         c.render(c.isLayout ? this.el : this.el.createChild());    
30162     },
30163     /**
30164      * Adds a object form elements (using the xtype property as the factory method.)
30165      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
30166      * @param {Object} config 
30167      */
30168     addxtype : function(o)
30169     {
30170         // create the lement.
30171         o.form = this.form;
30172         var fe = Roo.factory(o, Roo.form);
30173         this.form.allItems.push(fe);
30174         this.stack.push(fe);
30175         
30176         if (fe.isFormField) {
30177             this.form.items.add(fe);
30178         }
30179          
30180         return fe;
30181     }
30182 });
30183
30184 /**
30185  * @class Roo.form.Column
30186  * @extends Roo.form.Layout
30187  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
30188  * @constructor
30189  * @param {Object} config Configuration options
30190  */
30191 Roo.form.Column = function(config){
30192     Roo.form.Column.superclass.constructor.call(this, config);
30193 };
30194
30195 Roo.extend(Roo.form.Column, Roo.form.Layout, {
30196     /**
30197      * @cfg {Number/String} width
30198      * The fixed width of the column in pixels or CSS value (defaults to "auto")
30199      */
30200     /**
30201      * @cfg {String/Object} autoCreate
30202      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
30203      */
30204
30205     // private
30206     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
30207
30208     // private
30209     onRender : function(ct, position){
30210         Roo.form.Column.superclass.onRender.call(this, ct, position);
30211         if(this.width){
30212             this.el.setWidth(this.width);
30213         }
30214     }
30215 });
30216
30217
30218 /**
30219  * @class Roo.form.Row
30220  * @extends Roo.form.Layout
30221  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
30222  * @constructor
30223  * @param {Object} config Configuration options
30224  */
30225
30226  
30227 Roo.form.Row = function(config){
30228     Roo.form.Row.superclass.constructor.call(this, config);
30229 };
30230  
30231 Roo.extend(Roo.form.Row, Roo.form.Layout, {
30232       /**
30233      * @cfg {Number/String} width
30234      * The fixed width of the column in pixels or CSS value (defaults to "auto")
30235      */
30236     /**
30237      * @cfg {Number/String} height
30238      * The fixed height of the column in pixels or CSS value (defaults to "auto")
30239      */
30240     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
30241     
30242     padWidth : 20,
30243     // private
30244     onRender : function(ct, position){
30245         //console.log('row render');
30246         if(!this.rowTpl){
30247             var t = new Roo.Template(
30248                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
30249                     '<label for="{0}" style="{2}">{1}{4}</label>',
30250                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30251                     '</div>',
30252                 '</div>'
30253             );
30254             t.disableFormats = true;
30255             t.compile();
30256             Roo.form.Layout.prototype.rowTpl = t;
30257         }
30258         this.fieldTpl = this.rowTpl;
30259         
30260         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
30261         var labelWidth = 100;
30262         
30263         if ((this.labelAlign != 'top')) {
30264             if (typeof this.labelWidth == 'number') {
30265                 labelWidth = this.labelWidth
30266             }
30267             this.padWidth =  20 + labelWidth;
30268             
30269         }
30270         
30271         Roo.form.Column.superclass.onRender.call(this, ct, position);
30272         if(this.width){
30273             this.el.setWidth(this.width);
30274         }
30275         if(this.height){
30276             this.el.setHeight(this.height);
30277         }
30278     },
30279     
30280     // private
30281     renderField : function(f){
30282         f.fieldEl = this.fieldTpl.append(this.el, [
30283                f.id, f.fieldLabel,
30284                f.labelStyle||this.labelStyle||'',
30285                this.elementStyle||'',
30286                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
30287                f.itemCls||this.itemCls||'',
30288                f.width ? f.width + this.padWidth : 160 + this.padWidth
30289        ],true);
30290     }
30291 });
30292  
30293
30294 /**
30295  * @class Roo.form.FieldSet
30296  * @extends Roo.form.Layout
30297  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
30298  * @constructor
30299  * @param {Object} config Configuration options
30300  */
30301 Roo.form.FieldSet = function(config){
30302     Roo.form.FieldSet.superclass.constructor.call(this, config);
30303 };
30304
30305 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
30306     /**
30307      * @cfg {String} legend
30308      * The text to display as the legend for the FieldSet (defaults to '')
30309      */
30310     /**
30311      * @cfg {String/Object} autoCreate
30312      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
30313      */
30314
30315     // private
30316     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
30317
30318     // private
30319     onRender : function(ct, position){
30320         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
30321         if(this.legend){
30322             this.setLegend(this.legend);
30323         }
30324     },
30325
30326     // private
30327     setLegend : function(text){
30328         if(this.rendered){
30329             this.el.child('legend').update(text);
30330         }
30331     }
30332 });/*
30333  * Based on:
30334  * Ext JS Library 1.1.1
30335  * Copyright(c) 2006-2007, Ext JS, LLC.
30336  *
30337  * Originally Released Under LGPL - original licence link has changed is not relivant.
30338  *
30339  * Fork - LGPL
30340  * <script type="text/javascript">
30341  */
30342 /**
30343  * @class Roo.form.VTypes
30344  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
30345  * @singleton
30346  */
30347 Roo.form.VTypes = function(){
30348     // closure these in so they are only created once.
30349     var alpha = /^[a-zA-Z_]+$/;
30350     var alphanum = /^[a-zA-Z0-9_]+$/;
30351     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
30352     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
30353
30354     // All these messages and functions are configurable
30355     return {
30356         /**
30357          * The function used to validate email addresses
30358          * @param {String} value The email address
30359          */
30360         'email' : function(v){
30361             return email.test(v);
30362         },
30363         /**
30364          * The error text to display when the email validation function returns false
30365          * @type String
30366          */
30367         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
30368         /**
30369          * The keystroke filter mask to be applied on email input
30370          * @type RegExp
30371          */
30372         'emailMask' : /[a-z0-9_\.\-@]/i,
30373
30374         /**
30375          * The function used to validate URLs
30376          * @param {String} value The URL
30377          */
30378         'url' : function(v){
30379             return url.test(v);
30380         },
30381         /**
30382          * The error text to display when the url validation function returns false
30383          * @type String
30384          */
30385         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
30386         
30387         /**
30388          * The function used to validate alpha values
30389          * @param {String} value The value
30390          */
30391         'alpha' : function(v){
30392             return alpha.test(v);
30393         },
30394         /**
30395          * The error text to display when the alpha validation function returns false
30396          * @type String
30397          */
30398         'alphaText' : 'This field should only contain letters and _',
30399         /**
30400          * The keystroke filter mask to be applied on alpha input
30401          * @type RegExp
30402          */
30403         'alphaMask' : /[a-z_]/i,
30404
30405         /**
30406          * The function used to validate alphanumeric values
30407          * @param {String} value The value
30408          */
30409         'alphanum' : function(v){
30410             return alphanum.test(v);
30411         },
30412         /**
30413          * The error text to display when the alphanumeric validation function returns false
30414          * @type String
30415          */
30416         'alphanumText' : 'This field should only contain letters, numbers and _',
30417         /**
30418          * The keystroke filter mask to be applied on alphanumeric input
30419          * @type RegExp
30420          */
30421         'alphanumMask' : /[a-z0-9_]/i
30422     };
30423 }();//<script type="text/javascript">
30424
30425 /**
30426  * @class Roo.form.FCKeditor
30427  * @extends Roo.form.TextArea
30428  * Wrapper around the FCKEditor http://www.fckeditor.net
30429  * @constructor
30430  * Creates a new FCKeditor
30431  * @param {Object} config Configuration options
30432  */
30433 Roo.form.FCKeditor = function(config){
30434     Roo.form.FCKeditor.superclass.constructor.call(this, config);
30435     this.addEvents({
30436          /**
30437          * @event editorinit
30438          * Fired when the editor is initialized - you can add extra handlers here..
30439          * @param {FCKeditor} this
30440          * @param {Object} the FCK object.
30441          */
30442         editorinit : true
30443     });
30444     
30445     
30446 };
30447 Roo.form.FCKeditor.editors = { };
30448 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
30449 {
30450     //defaultAutoCreate : {
30451     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
30452     //},
30453     // private
30454     /**
30455      * @cfg {Object} fck options - see fck manual for details.
30456      */
30457     fckconfig : false,
30458     
30459     /**
30460      * @cfg {Object} fck toolbar set (Basic or Default)
30461      */
30462     toolbarSet : 'Basic',
30463     /**
30464      * @cfg {Object} fck BasePath
30465      */ 
30466     basePath : '/fckeditor/',
30467     
30468     
30469     frame : false,
30470     
30471     value : '',
30472     
30473    
30474     onRender : function(ct, position)
30475     {
30476         if(!this.el){
30477             this.defaultAutoCreate = {
30478                 tag: "textarea",
30479                 style:"width:300px;height:60px;",
30480                 autocomplete: "new-password"
30481             };
30482         }
30483         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
30484         /*
30485         if(this.grow){
30486             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
30487             if(this.preventScrollbars){
30488                 this.el.setStyle("overflow", "hidden");
30489             }
30490             this.el.setHeight(this.growMin);
30491         }
30492         */
30493         //console.log('onrender' + this.getId() );
30494         Roo.form.FCKeditor.editors[this.getId()] = this;
30495          
30496
30497         this.replaceTextarea() ;
30498         
30499     },
30500     
30501     getEditor : function() {
30502         return this.fckEditor;
30503     },
30504     /**
30505      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
30506      * @param {Mixed} value The value to set
30507      */
30508     
30509     
30510     setValue : function(value)
30511     {
30512         //console.log('setValue: ' + value);
30513         
30514         if(typeof(value) == 'undefined') { // not sure why this is happending...
30515             return;
30516         }
30517         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
30518         
30519         //if(!this.el || !this.getEditor()) {
30520         //    this.value = value;
30521             //this.setValue.defer(100,this,[value]);    
30522         //    return;
30523         //} 
30524         
30525         if(!this.getEditor()) {
30526             return;
30527         }
30528         
30529         this.getEditor().SetData(value);
30530         
30531         //
30532
30533     },
30534
30535     /**
30536      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
30537      * @return {Mixed} value The field value
30538      */
30539     getValue : function()
30540     {
30541         
30542         if (this.frame && this.frame.dom.style.display == 'none') {
30543             return Roo.form.FCKeditor.superclass.getValue.call(this);
30544         }
30545         
30546         if(!this.el || !this.getEditor()) {
30547            
30548            // this.getValue.defer(100,this); 
30549             return this.value;
30550         }
30551        
30552         
30553         var value=this.getEditor().GetData();
30554         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
30555         return Roo.form.FCKeditor.superclass.getValue.call(this);
30556         
30557
30558     },
30559
30560     /**
30561      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
30562      * @return {Mixed} value The field value
30563      */
30564     getRawValue : function()
30565     {
30566         if (this.frame && this.frame.dom.style.display == 'none') {
30567             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30568         }
30569         
30570         if(!this.el || !this.getEditor()) {
30571             //this.getRawValue.defer(100,this); 
30572             return this.value;
30573             return;
30574         }
30575         
30576         
30577         
30578         var value=this.getEditor().GetData();
30579         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
30580         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30581          
30582     },
30583     
30584     setSize : function(w,h) {
30585         
30586         
30587         
30588         //if (this.frame && this.frame.dom.style.display == 'none') {
30589         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30590         //    return;
30591         //}
30592         //if(!this.el || !this.getEditor()) {
30593         //    this.setSize.defer(100,this, [w,h]); 
30594         //    return;
30595         //}
30596         
30597         
30598         
30599         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30600         
30601         this.frame.dom.setAttribute('width', w);
30602         this.frame.dom.setAttribute('height', h);
30603         this.frame.setSize(w,h);
30604         
30605     },
30606     
30607     toggleSourceEdit : function(value) {
30608         
30609       
30610          
30611         this.el.dom.style.display = value ? '' : 'none';
30612         this.frame.dom.style.display = value ?  'none' : '';
30613         
30614     },
30615     
30616     
30617     focus: function(tag)
30618     {
30619         if (this.frame.dom.style.display == 'none') {
30620             return Roo.form.FCKeditor.superclass.focus.call(this);
30621         }
30622         if(!this.el || !this.getEditor()) {
30623             this.focus.defer(100,this, [tag]); 
30624             return;
30625         }
30626         
30627         
30628         
30629         
30630         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
30631         this.getEditor().Focus();
30632         if (tgs.length) {
30633             if (!this.getEditor().Selection.GetSelection()) {
30634                 this.focus.defer(100,this, [tag]); 
30635                 return;
30636             }
30637             
30638             
30639             var r = this.getEditor().EditorDocument.createRange();
30640             r.setStart(tgs[0],0);
30641             r.setEnd(tgs[0],0);
30642             this.getEditor().Selection.GetSelection().removeAllRanges();
30643             this.getEditor().Selection.GetSelection().addRange(r);
30644             this.getEditor().Focus();
30645         }
30646         
30647     },
30648     
30649     
30650     
30651     replaceTextarea : function()
30652     {
30653         if ( document.getElementById( this.getId() + '___Frame' ) ) {
30654             return ;
30655         }
30656         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
30657         //{
30658             // We must check the elements firstly using the Id and then the name.
30659         var oTextarea = document.getElementById( this.getId() );
30660         
30661         var colElementsByName = document.getElementsByName( this.getId() ) ;
30662          
30663         oTextarea.style.display = 'none' ;
30664
30665         if ( oTextarea.tabIndex ) {            
30666             this.TabIndex = oTextarea.tabIndex ;
30667         }
30668         
30669         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
30670         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
30671         this.frame = Roo.get(this.getId() + '___Frame')
30672     },
30673     
30674     _getConfigHtml : function()
30675     {
30676         var sConfig = '' ;
30677
30678         for ( var o in this.fckconfig ) {
30679             sConfig += sConfig.length > 0  ? '&amp;' : '';
30680             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
30681         }
30682
30683         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
30684     },
30685     
30686     
30687     _getIFrameHtml : function()
30688     {
30689         var sFile = 'fckeditor.html' ;
30690         /* no idea what this is about..
30691         try
30692         {
30693             if ( (/fcksource=true/i).test( window.top.location.search ) )
30694                 sFile = 'fckeditor.original.html' ;
30695         }
30696         catch (e) { 
30697         */
30698
30699         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
30700         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
30701         
30702         
30703         var html = '<iframe id="' + this.getId() +
30704             '___Frame" src="' + sLink +
30705             '" width="' + this.width +
30706             '" height="' + this.height + '"' +
30707             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
30708             ' frameborder="0" scrolling="no"></iframe>' ;
30709
30710         return html ;
30711     },
30712     
30713     _insertHtmlBefore : function( html, element )
30714     {
30715         if ( element.insertAdjacentHTML )       {
30716             // IE
30717             element.insertAdjacentHTML( 'beforeBegin', html ) ;
30718         } else { // Gecko
30719             var oRange = document.createRange() ;
30720             oRange.setStartBefore( element ) ;
30721             var oFragment = oRange.createContextualFragment( html );
30722             element.parentNode.insertBefore( oFragment, element ) ;
30723         }
30724     }
30725     
30726     
30727   
30728     
30729     
30730     
30731     
30732
30733 });
30734
30735 //Roo.reg('fckeditor', Roo.form.FCKeditor);
30736
30737 function FCKeditor_OnComplete(editorInstance){
30738     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
30739     f.fckEditor = editorInstance;
30740     //console.log("loaded");
30741     f.fireEvent('editorinit', f, editorInstance);
30742
30743   
30744
30745  
30746
30747
30748
30749
30750
30751
30752
30753
30754
30755
30756
30757
30758
30759
30760
30761 //<script type="text/javascript">
30762 /**
30763  * @class Roo.form.GridField
30764  * @extends Roo.form.Field
30765  * Embed a grid (or editable grid into a form)
30766  * STATUS ALPHA
30767  * 
30768  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
30769  * it needs 
30770  * xgrid.store = Roo.data.Store
30771  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
30772  * xgrid.store.reader = Roo.data.JsonReader 
30773  * 
30774  * 
30775  * @constructor
30776  * Creates a new GridField
30777  * @param {Object} config Configuration options
30778  */
30779 Roo.form.GridField = function(config){
30780     Roo.form.GridField.superclass.constructor.call(this, config);
30781      
30782 };
30783
30784 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
30785     /**
30786      * @cfg {Number} width  - used to restrict width of grid..
30787      */
30788     width : 100,
30789     /**
30790      * @cfg {Number} height - used to restrict height of grid..
30791      */
30792     height : 50,
30793      /**
30794      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
30795          * 
30796          *}
30797      */
30798     xgrid : false, 
30799     /**
30800      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30801      * {tag: "input", type: "checkbox", autocomplete: "off"})
30802      */
30803    // defaultAutoCreate : { tag: 'div' },
30804     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
30805     /**
30806      * @cfg {String} addTitle Text to include for adding a title.
30807      */
30808     addTitle : false,
30809     //
30810     onResize : function(){
30811         Roo.form.Field.superclass.onResize.apply(this, arguments);
30812     },
30813
30814     initEvents : function(){
30815         // Roo.form.Checkbox.superclass.initEvents.call(this);
30816         // has no events...
30817        
30818     },
30819
30820
30821     getResizeEl : function(){
30822         return this.wrap;
30823     },
30824
30825     getPositionEl : function(){
30826         return this.wrap;
30827     },
30828
30829     // private
30830     onRender : function(ct, position){
30831         
30832         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
30833         var style = this.style;
30834         delete this.style;
30835         
30836         Roo.form.GridField.superclass.onRender.call(this, ct, position);
30837         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
30838         this.viewEl = this.wrap.createChild({ tag: 'div' });
30839         if (style) {
30840             this.viewEl.applyStyles(style);
30841         }
30842         if (this.width) {
30843             this.viewEl.setWidth(this.width);
30844         }
30845         if (this.height) {
30846             this.viewEl.setHeight(this.height);
30847         }
30848         //if(this.inputValue !== undefined){
30849         //this.setValue(this.value);
30850         
30851         
30852         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
30853         
30854         
30855         this.grid.render();
30856         this.grid.getDataSource().on('remove', this.refreshValue, this);
30857         this.grid.getDataSource().on('update', this.refreshValue, this);
30858         this.grid.on('afteredit', this.refreshValue, this);
30859  
30860     },
30861      
30862     
30863     /**
30864      * Sets the value of the item. 
30865      * @param {String} either an object  or a string..
30866      */
30867     setValue : function(v){
30868         //this.value = v;
30869         v = v || []; // empty set..
30870         // this does not seem smart - it really only affects memoryproxy grids..
30871         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
30872             var ds = this.grid.getDataSource();
30873             // assumes a json reader..
30874             var data = {}
30875             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
30876             ds.loadData( data);
30877         }
30878         // clear selection so it does not get stale.
30879         if (this.grid.sm) { 
30880             this.grid.sm.clearSelections();
30881         }
30882         
30883         Roo.form.GridField.superclass.setValue.call(this, v);
30884         this.refreshValue();
30885         // should load data in the grid really....
30886     },
30887     
30888     // private
30889     refreshValue: function() {
30890          var val = [];
30891         this.grid.getDataSource().each(function(r) {
30892             val.push(r.data);
30893         });
30894         this.el.dom.value = Roo.encode(val);
30895     }
30896     
30897      
30898     
30899     
30900 });/*
30901  * Based on:
30902  * Ext JS Library 1.1.1
30903  * Copyright(c) 2006-2007, Ext JS, LLC.
30904  *
30905  * Originally Released Under LGPL - original licence link has changed is not relivant.
30906  *
30907  * Fork - LGPL
30908  * <script type="text/javascript">
30909  */
30910 /**
30911  * @class Roo.form.DisplayField
30912  * @extends Roo.form.Field
30913  * A generic Field to display non-editable data.
30914  * @cfg {Boolean} closable (true|false) default false
30915  * @constructor
30916  * Creates a new Display Field item.
30917  * @param {Object} config Configuration options
30918  */
30919 Roo.form.DisplayField = function(config){
30920     Roo.form.DisplayField.superclass.constructor.call(this, config);
30921     
30922     this.addEvents({
30923         /**
30924          * @event close
30925          * Fires after the click the close btn
30926              * @param {Roo.form.DisplayField} this
30927              */
30928         close : true
30929     });
30930 };
30931
30932 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
30933     inputType:      'hidden',
30934     allowBlank:     true,
30935     readOnly:         true,
30936     
30937  
30938     /**
30939      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30940      */
30941     focusClass : undefined,
30942     /**
30943      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30944      */
30945     fieldClass: 'x-form-field',
30946     
30947      /**
30948      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
30949      */
30950     valueRenderer: undefined,
30951     
30952     width: 100,
30953     /**
30954      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30955      * {tag: "input", type: "checkbox", autocomplete: "off"})
30956      */
30957      
30958  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30959  
30960     closable : false,
30961     
30962     onResize : function(){
30963         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
30964         
30965     },
30966
30967     initEvents : function(){
30968         // Roo.form.Checkbox.superclass.initEvents.call(this);
30969         // has no events...
30970         
30971         if(this.closable){
30972             this.closeEl.on('click', this.onClose, this);
30973         }
30974        
30975     },
30976
30977
30978     getResizeEl : function(){
30979         return this.wrap;
30980     },
30981
30982     getPositionEl : function(){
30983         return this.wrap;
30984     },
30985
30986     // private
30987     onRender : function(ct, position){
30988         
30989         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
30990         //if(this.inputValue !== undefined){
30991         this.wrap = this.el.wrap();
30992         
30993         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
30994         
30995         if(this.closable){
30996             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
30997         }
30998         
30999         if (this.bodyStyle) {
31000             this.viewEl.applyStyles(this.bodyStyle);
31001         }
31002         //this.viewEl.setStyle('padding', '2px');
31003         
31004         this.setValue(this.value);
31005         
31006     },
31007 /*
31008     // private
31009     initValue : Roo.emptyFn,
31010
31011   */
31012
31013         // private
31014     onClick : function(){
31015         
31016     },
31017
31018     /**
31019      * Sets the checked state of the checkbox.
31020      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
31021      */
31022     setValue : function(v){
31023         this.value = v;
31024         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
31025         // this might be called before we have a dom element..
31026         if (!this.viewEl) {
31027             return;
31028         }
31029         this.viewEl.dom.innerHTML = html;
31030         Roo.form.DisplayField.superclass.setValue.call(this, v);
31031
31032     },
31033     
31034     onClose : function(e)
31035     {
31036         e.preventDefault();
31037         
31038         this.fireEvent('close', this);
31039     }
31040 });/*
31041  * 
31042  * Licence- LGPL
31043  * 
31044  */
31045
31046 /**
31047  * @class Roo.form.DayPicker
31048  * @extends Roo.form.Field
31049  * A Day picker show [M] [T] [W] ....
31050  * @constructor
31051  * Creates a new Day Picker
31052  * @param {Object} config Configuration options
31053  */
31054 Roo.form.DayPicker= function(config){
31055     Roo.form.DayPicker.superclass.constructor.call(this, config);
31056      
31057 };
31058
31059 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
31060     /**
31061      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
31062      */
31063     focusClass : undefined,
31064     /**
31065      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
31066      */
31067     fieldClass: "x-form-field",
31068    
31069     /**
31070      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
31071      * {tag: "input", type: "checkbox", autocomplete: "off"})
31072      */
31073     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
31074     
31075    
31076     actionMode : 'viewEl', 
31077     //
31078     // private
31079  
31080     inputType : 'hidden',
31081     
31082      
31083     inputElement: false, // real input element?
31084     basedOn: false, // ????
31085     
31086     isFormField: true, // not sure where this is needed!!!!
31087
31088     onResize : function(){
31089         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
31090         if(!this.boxLabel){
31091             this.el.alignTo(this.wrap, 'c-c');
31092         }
31093     },
31094
31095     initEvents : function(){
31096         Roo.form.Checkbox.superclass.initEvents.call(this);
31097         this.el.on("click", this.onClick,  this);
31098         this.el.on("change", this.onClick,  this);
31099     },
31100
31101
31102     getResizeEl : function(){
31103         return this.wrap;
31104     },
31105
31106     getPositionEl : function(){
31107         return this.wrap;
31108     },
31109
31110     
31111     // private
31112     onRender : function(ct, position){
31113         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
31114        
31115         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
31116         
31117         var r1 = '<table><tr>';
31118         var r2 = '<tr class="x-form-daypick-icons">';
31119         for (var i=0; i < 7; i++) {
31120             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
31121             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
31122         }
31123         
31124         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
31125         viewEl.select('img').on('click', this.onClick, this);
31126         this.viewEl = viewEl;   
31127         
31128         
31129         // this will not work on Chrome!!!
31130         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
31131         this.el.on('propertychange', this.setFromHidden,  this);  //ie
31132         
31133         
31134           
31135
31136     },
31137
31138     // private
31139     initValue : Roo.emptyFn,
31140
31141     /**
31142      * Returns the checked state of the checkbox.
31143      * @return {Boolean} True if checked, else false
31144      */
31145     getValue : function(){
31146         return this.el.dom.value;
31147         
31148     },
31149
31150         // private
31151     onClick : function(e){ 
31152         //this.setChecked(!this.checked);
31153         Roo.get(e.target).toggleClass('x-menu-item-checked');
31154         this.refreshValue();
31155         //if(this.el.dom.checked != this.checked){
31156         //    this.setValue(this.el.dom.checked);
31157        // }
31158     },
31159     
31160     // private
31161     refreshValue : function()
31162     {
31163         var val = '';
31164         this.viewEl.select('img',true).each(function(e,i,n)  {
31165             val += e.is(".x-menu-item-checked") ? String(n) : '';
31166         });
31167         this.setValue(val, true);
31168     },
31169
31170     /**
31171      * Sets the checked state of the checkbox.
31172      * On is always based on a string comparison between inputValue and the param.
31173      * @param {Boolean/String} value - the value to set 
31174      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
31175      */
31176     setValue : function(v,suppressEvent){
31177         if (!this.el.dom) {
31178             return;
31179         }
31180         var old = this.el.dom.value ;
31181         this.el.dom.value = v;
31182         if (suppressEvent) {
31183             return ;
31184         }
31185          
31186         // update display..
31187         this.viewEl.select('img',true).each(function(e,i,n)  {
31188             
31189             var on = e.is(".x-menu-item-checked");
31190             var newv = v.indexOf(String(n)) > -1;
31191             if (on != newv) {
31192                 e.toggleClass('x-menu-item-checked');
31193             }
31194             
31195         });
31196         
31197         
31198         this.fireEvent('change', this, v, old);
31199         
31200         
31201     },
31202    
31203     // handle setting of hidden value by some other method!!?!?
31204     setFromHidden: function()
31205     {
31206         if(!this.el){
31207             return;
31208         }
31209         //console.log("SET FROM HIDDEN");
31210         //alert('setFrom hidden');
31211         this.setValue(this.el.dom.value);
31212     },
31213     
31214     onDestroy : function()
31215     {
31216         if(this.viewEl){
31217             Roo.get(this.viewEl).remove();
31218         }
31219          
31220         Roo.form.DayPicker.superclass.onDestroy.call(this);
31221     }
31222
31223 });/*
31224  * RooJS Library 1.1.1
31225  * Copyright(c) 2008-2011  Alan Knowles
31226  *
31227  * License - LGPL
31228  */
31229  
31230
31231 /**
31232  * @class Roo.form.ComboCheck
31233  * @extends Roo.form.ComboBox
31234  * A combobox for multiple select items.
31235  *
31236  * FIXME - could do with a reset button..
31237  * 
31238  * @constructor
31239  * Create a new ComboCheck
31240  * @param {Object} config Configuration options
31241  */
31242 Roo.form.ComboCheck = function(config){
31243     Roo.form.ComboCheck.superclass.constructor.call(this, config);
31244     // should verify some data...
31245     // like
31246     // hiddenName = required..
31247     // displayField = required
31248     // valudField == required
31249     var req= [ 'hiddenName', 'displayField', 'valueField' ];
31250     var _t = this;
31251     Roo.each(req, function(e) {
31252         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
31253             throw "Roo.form.ComboCheck : missing value for: " + e;
31254         }
31255     });
31256     
31257     
31258 };
31259
31260 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
31261      
31262      
31263     editable : false,
31264      
31265     selectedClass: 'x-menu-item-checked', 
31266     
31267     // private
31268     onRender : function(ct, position){
31269         var _t = this;
31270         
31271         
31272         
31273         if(!this.tpl){
31274             var cls = 'x-combo-list';
31275
31276             
31277             this.tpl =  new Roo.Template({
31278                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
31279                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
31280                    '<span>{' + this.displayField + '}</span>' +
31281                     '</div>' 
31282                 
31283             });
31284         }
31285  
31286         
31287         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
31288         this.view.singleSelect = false;
31289         this.view.multiSelect = true;
31290         this.view.toggleSelect = true;
31291         this.pageTb.add(new Roo.Toolbar.Fill(), {
31292             
31293             text: 'Done',
31294             handler: function()
31295             {
31296                 _t.collapse();
31297             }
31298         });
31299     },
31300     
31301     onViewOver : function(e, t){
31302         // do nothing...
31303         return;
31304         
31305     },
31306     
31307     onViewClick : function(doFocus,index){
31308         return;
31309         
31310     },
31311     select: function () {
31312         //Roo.log("SELECT CALLED");
31313     },
31314      
31315     selectByValue : function(xv, scrollIntoView){
31316         var ar = this.getValueArray();
31317         var sels = [];
31318         
31319         Roo.each(ar, function(v) {
31320             if(v === undefined || v === null){
31321                 return;
31322             }
31323             var r = this.findRecord(this.valueField, v);
31324             if(r){
31325                 sels.push(this.store.indexOf(r))
31326                 
31327             }
31328         },this);
31329         this.view.select(sels);
31330         return false;
31331     },
31332     
31333     
31334     
31335     onSelect : function(record, index){
31336        // Roo.log("onselect Called");
31337        // this is only called by the clear button now..
31338         this.view.clearSelections();
31339         this.setValue('[]');
31340         if (this.value != this.valueBefore) {
31341             this.fireEvent('change', this, this.value, this.valueBefore);
31342             this.valueBefore = this.value;
31343         }
31344     },
31345     getValueArray : function()
31346     {
31347         var ar = [] ;
31348         
31349         try {
31350             //Roo.log(this.value);
31351             if (typeof(this.value) == 'undefined') {
31352                 return [];
31353             }
31354             var ar = Roo.decode(this.value);
31355             return  ar instanceof Array ? ar : []; //?? valid?
31356             
31357         } catch(e) {
31358             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
31359             return [];
31360         }
31361          
31362     },
31363     expand : function ()
31364     {
31365         
31366         Roo.form.ComboCheck.superclass.expand.call(this);
31367         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
31368         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
31369         
31370
31371     },
31372     
31373     collapse : function(){
31374         Roo.form.ComboCheck.superclass.collapse.call(this);
31375         var sl = this.view.getSelectedIndexes();
31376         var st = this.store;
31377         var nv = [];
31378         var tv = [];
31379         var r;
31380         Roo.each(sl, function(i) {
31381             r = st.getAt(i);
31382             nv.push(r.get(this.valueField));
31383         },this);
31384         this.setValue(Roo.encode(nv));
31385         if (this.value != this.valueBefore) {
31386
31387             this.fireEvent('change', this, this.value, this.valueBefore);
31388             this.valueBefore = this.value;
31389         }
31390         
31391     },
31392     
31393     setValue : function(v){
31394         // Roo.log(v);
31395         this.value = v;
31396         
31397         var vals = this.getValueArray();
31398         var tv = [];
31399         Roo.each(vals, function(k) {
31400             var r = this.findRecord(this.valueField, k);
31401             if(r){
31402                 tv.push(r.data[this.displayField]);
31403             }else if(this.valueNotFoundText !== undefined){
31404                 tv.push( this.valueNotFoundText );
31405             }
31406         },this);
31407        // Roo.log(tv);
31408         
31409         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
31410         this.hiddenField.value = v;
31411         this.value = v;
31412     }
31413     
31414 });/*
31415  * Based on:
31416  * Ext JS Library 1.1.1
31417  * Copyright(c) 2006-2007, Ext JS, LLC.
31418  *
31419  * Originally Released Under LGPL - original licence link has changed is not relivant.
31420  *
31421  * Fork - LGPL
31422  * <script type="text/javascript">
31423  */
31424  
31425 /**
31426  * @class Roo.form.Signature
31427  * @extends Roo.form.Field
31428  * Signature field.  
31429  * @constructor
31430  * 
31431  * @param {Object} config Configuration options
31432  */
31433
31434 Roo.form.Signature = function(config){
31435     Roo.form.Signature.superclass.constructor.call(this, config);
31436     
31437     this.addEvents({// not in used??
31438          /**
31439          * @event confirm
31440          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
31441              * @param {Roo.form.Signature} combo This combo box
31442              */
31443         'confirm' : true,
31444         /**
31445          * @event reset
31446          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
31447              * @param {Roo.form.ComboBox} combo This combo box
31448              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
31449              */
31450         'reset' : true
31451     });
31452 };
31453
31454 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
31455     /**
31456      * @cfg {Object} labels Label to use when rendering a form.
31457      * defaults to 
31458      * labels : { 
31459      *      clear : "Clear",
31460      *      confirm : "Confirm"
31461      *  }
31462      */
31463     labels : { 
31464         clear : "Clear",
31465         confirm : "Confirm"
31466     },
31467     /**
31468      * @cfg {Number} width The signature panel width (defaults to 300)
31469      */
31470     width: 300,
31471     /**
31472      * @cfg {Number} height The signature panel height (defaults to 100)
31473      */
31474     height : 100,
31475     /**
31476      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
31477      */
31478     allowBlank : false,
31479     
31480     //private
31481     // {Object} signPanel The signature SVG panel element (defaults to {})
31482     signPanel : {},
31483     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
31484     isMouseDown : false,
31485     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
31486     isConfirmed : false,
31487     // {String} signatureTmp SVG mapping string (defaults to empty string)
31488     signatureTmp : '',
31489     
31490     
31491     defaultAutoCreate : { // modified by initCompnoent..
31492         tag: "input",
31493         type:"hidden"
31494     },
31495
31496     // private
31497     onRender : function(ct, position){
31498         
31499         Roo.form.Signature.superclass.onRender.call(this, ct, position);
31500         
31501         this.wrap = this.el.wrap({
31502             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
31503         });
31504         
31505         this.createToolbar(this);
31506         this.signPanel = this.wrap.createChild({
31507                 tag: 'div',
31508                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
31509             }, this.el
31510         );
31511             
31512         this.svgID = Roo.id();
31513         this.svgEl = this.signPanel.createChild({
31514               xmlns : 'http://www.w3.org/2000/svg',
31515               tag : 'svg',
31516               id : this.svgID + "-svg",
31517               width: this.width,
31518               height: this.height,
31519               viewBox: '0 0 '+this.width+' '+this.height,
31520               cn : [
31521                 {
31522                     tag: "rect",
31523                     id: this.svgID + "-svg-r",
31524                     width: this.width,
31525                     height: this.height,
31526                     fill: "#ffa"
31527                 },
31528                 {
31529                     tag: "line",
31530                     id: this.svgID + "-svg-l",
31531                     x1: "0", // start
31532                     y1: (this.height*0.8), // start set the line in 80% of height
31533                     x2: this.width, // end
31534                     y2: (this.height*0.8), // end set the line in 80% of height
31535                     'stroke': "#666",
31536                     'stroke-width': "1",
31537                     'stroke-dasharray': "3",
31538                     'shape-rendering': "crispEdges",
31539                     'pointer-events': "none"
31540                 },
31541                 {
31542                     tag: "path",
31543                     id: this.svgID + "-svg-p",
31544                     'stroke': "navy",
31545                     'stroke-width': "3",
31546                     'fill': "none",
31547                     'pointer-events': 'none'
31548                 }
31549               ]
31550         });
31551         this.createSVG();
31552         this.svgBox = this.svgEl.dom.getScreenCTM();
31553     },
31554     createSVG : function(){ 
31555         var svg = this.signPanel;
31556         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
31557         var t = this;
31558
31559         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
31560         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
31561         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
31562         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
31563         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
31564         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
31565         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
31566         
31567     },
31568     isTouchEvent : function(e){
31569         return e.type.match(/^touch/);
31570     },
31571     getCoords : function (e) {
31572         var pt    = this.svgEl.dom.createSVGPoint();
31573         pt.x = e.clientX; 
31574         pt.y = e.clientY;
31575         if (this.isTouchEvent(e)) {
31576             pt.x =  e.targetTouches[0].clientX;
31577             pt.y = e.targetTouches[0].clientY;
31578         }
31579         var a = this.svgEl.dom.getScreenCTM();
31580         var b = a.inverse();
31581         var mx = pt.matrixTransform(b);
31582         return mx.x + ',' + mx.y;
31583     },
31584     //mouse event headler 
31585     down : function (e) {
31586         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
31587         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
31588         
31589         this.isMouseDown = true;
31590         
31591         e.preventDefault();
31592     },
31593     move : function (e) {
31594         if (this.isMouseDown) {
31595             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
31596             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
31597         }
31598         
31599         e.preventDefault();
31600     },
31601     up : function (e) {
31602         this.isMouseDown = false;
31603         var sp = this.signatureTmp.split(' ');
31604         
31605         if(sp.length > 1){
31606             if(!sp[sp.length-2].match(/^L/)){
31607                 sp.pop();
31608                 sp.pop();
31609                 sp.push("");
31610                 this.signatureTmp = sp.join(" ");
31611             }
31612         }
31613         if(this.getValue() != this.signatureTmp){
31614             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31615             this.isConfirmed = false;
31616         }
31617         e.preventDefault();
31618     },
31619     
31620     /**
31621      * Protected method that will not generally be called directly. It
31622      * is called when the editor creates its toolbar. Override this method if you need to
31623      * add custom toolbar buttons.
31624      * @param {HtmlEditor} editor
31625      */
31626     createToolbar : function(editor){
31627          function btn(id, toggle, handler){
31628             var xid = fid + '-'+ id ;
31629             return {
31630                 id : xid,
31631                 cmd : id,
31632                 cls : 'x-btn-icon x-edit-'+id,
31633                 enableToggle:toggle !== false,
31634                 scope: editor, // was editor...
31635                 handler:handler||editor.relayBtnCmd,
31636                 clickEvent:'mousedown',
31637                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
31638                 tabIndex:-1
31639             };
31640         }
31641         
31642         
31643         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
31644         this.tb = tb;
31645         this.tb.add(
31646            {
31647                 cls : ' x-signature-btn x-signature-'+id,
31648                 scope: editor, // was editor...
31649                 handler: this.reset,
31650                 clickEvent:'mousedown',
31651                 text: this.labels.clear
31652             },
31653             {
31654                  xtype : 'Fill',
31655                  xns: Roo.Toolbar
31656             }, 
31657             {
31658                 cls : '  x-signature-btn x-signature-'+id,
31659                 scope: editor, // was editor...
31660                 handler: this.confirmHandler,
31661                 clickEvent:'mousedown',
31662                 text: this.labels.confirm
31663             }
31664         );
31665     
31666     },
31667     //public
31668     /**
31669      * when user is clicked confirm then show this image.....
31670      * 
31671      * @return {String} Image Data URI
31672      */
31673     getImageDataURI : function(){
31674         var svg = this.svgEl.dom.parentNode.innerHTML;
31675         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
31676         return src; 
31677     },
31678     /**
31679      * 
31680      * @return {Boolean} this.isConfirmed
31681      */
31682     getConfirmed : function(){
31683         return this.isConfirmed;
31684     },
31685     /**
31686      * 
31687      * @return {Number} this.width
31688      */
31689     getWidth : function(){
31690         return this.width;
31691     },
31692     /**
31693      * 
31694      * @return {Number} this.height
31695      */
31696     getHeight : function(){
31697         return this.height;
31698     },
31699     // private
31700     getSignature : function(){
31701         return this.signatureTmp;
31702     },
31703     // private
31704     reset : function(){
31705         this.signatureTmp = '';
31706         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31707         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
31708         this.isConfirmed = false;
31709         Roo.form.Signature.superclass.reset.call(this);
31710     },
31711     setSignature : function(s){
31712         this.signatureTmp = s;
31713         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31714         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
31715         this.setValue(s);
31716         this.isConfirmed = false;
31717         Roo.form.Signature.superclass.reset.call(this);
31718     }, 
31719     test : function(){
31720 //        Roo.log(this.signPanel.dom.contentWindow.up())
31721     },
31722     //private
31723     setConfirmed : function(){
31724         
31725         
31726         
31727 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
31728     },
31729     // private
31730     confirmHandler : function(){
31731         if(!this.getSignature()){
31732             return;
31733         }
31734         
31735         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
31736         this.setValue(this.getSignature());
31737         this.isConfirmed = true;
31738         
31739         this.fireEvent('confirm', this);
31740     },
31741     // private
31742     // Subclasses should provide the validation implementation by overriding this
31743     validateValue : function(value){
31744         if(this.allowBlank){
31745             return true;
31746         }
31747         
31748         if(this.isConfirmed){
31749             return true;
31750         }
31751         return false;
31752     }
31753 });/*
31754  * Based on:
31755  * Ext JS Library 1.1.1
31756  * Copyright(c) 2006-2007, Ext JS, LLC.
31757  *
31758  * Originally Released Under LGPL - original licence link has changed is not relivant.
31759  *
31760  * Fork - LGPL
31761  * <script type="text/javascript">
31762  */
31763  
31764
31765 /**
31766  * @class Roo.form.ComboBox
31767  * @extends Roo.form.TriggerField
31768  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
31769  * @constructor
31770  * Create a new ComboBox.
31771  * @param {Object} config Configuration options
31772  */
31773 Roo.form.Select = function(config){
31774     Roo.form.Select.superclass.constructor.call(this, config);
31775      
31776 };
31777
31778 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
31779     /**
31780      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
31781      */
31782     /**
31783      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
31784      * rendering into an Roo.Editor, defaults to false)
31785      */
31786     /**
31787      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
31788      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
31789      */
31790     /**
31791      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
31792      */
31793     /**
31794      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
31795      * the dropdown list (defaults to undefined, with no header element)
31796      */
31797
31798      /**
31799      * @cfg {String/Roo.Template} tpl The template to use to render the output
31800      */
31801      
31802     // private
31803     defaultAutoCreate : {tag: "select"  },
31804     /**
31805      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
31806      */
31807     listWidth: undefined,
31808     /**
31809      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
31810      * mode = 'remote' or 'text' if mode = 'local')
31811      */
31812     displayField: undefined,
31813     /**
31814      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
31815      * mode = 'remote' or 'value' if mode = 'local'). 
31816      * Note: use of a valueField requires the user make a selection
31817      * in order for a value to be mapped.
31818      */
31819     valueField: undefined,
31820     
31821     
31822     /**
31823      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
31824      * field's data value (defaults to the underlying DOM element's name)
31825      */
31826     hiddenName: undefined,
31827     /**
31828      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
31829      */
31830     listClass: '',
31831     /**
31832      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
31833      */
31834     selectedClass: 'x-combo-selected',
31835     /**
31836      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
31837      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
31838      * which displays a downward arrow icon).
31839      */
31840     triggerClass : 'x-form-arrow-trigger',
31841     /**
31842      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31843      */
31844     shadow:'sides',
31845     /**
31846      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
31847      * anchor positions (defaults to 'tl-bl')
31848      */
31849     listAlign: 'tl-bl?',
31850     /**
31851      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
31852      */
31853     maxHeight: 300,
31854     /**
31855      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
31856      * query specified by the allQuery config option (defaults to 'query')
31857      */
31858     triggerAction: 'query',
31859     /**
31860      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
31861      * (defaults to 4, does not apply if editable = false)
31862      */
31863     minChars : 4,
31864     /**
31865      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
31866      * delay (typeAheadDelay) if it matches a known value (defaults to false)
31867      */
31868     typeAhead: false,
31869     /**
31870      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
31871      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
31872      */
31873     queryDelay: 500,
31874     /**
31875      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
31876      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
31877      */
31878     pageSize: 0,
31879     /**
31880      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
31881      * when editable = true (defaults to false)
31882      */
31883     selectOnFocus:false,
31884     /**
31885      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
31886      */
31887     queryParam: 'query',
31888     /**
31889      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
31890      * when mode = 'remote' (defaults to 'Loading...')
31891      */
31892     loadingText: 'Loading...',
31893     /**
31894      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
31895      */
31896     resizable: false,
31897     /**
31898      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
31899      */
31900     handleHeight : 8,
31901     /**
31902      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
31903      * traditional select (defaults to true)
31904      */
31905     editable: true,
31906     /**
31907      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
31908      */
31909     allQuery: '',
31910     /**
31911      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
31912      */
31913     mode: 'remote',
31914     /**
31915      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
31916      * listWidth has a higher value)
31917      */
31918     minListWidth : 70,
31919     /**
31920      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
31921      * allow the user to set arbitrary text into the field (defaults to false)
31922      */
31923     forceSelection:false,
31924     /**
31925      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
31926      * if typeAhead = true (defaults to 250)
31927      */
31928     typeAheadDelay : 250,
31929     /**
31930      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
31931      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
31932      */
31933     valueNotFoundText : undefined,
31934     
31935     /**
31936      * @cfg {String} defaultValue The value displayed after loading the store.
31937      */
31938     defaultValue: '',
31939     
31940     /**
31941      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
31942      */
31943     blockFocus : false,
31944     
31945     /**
31946      * @cfg {Boolean} disableClear Disable showing of clear button.
31947      */
31948     disableClear : false,
31949     /**
31950      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
31951      */
31952     alwaysQuery : false,
31953     
31954     //private
31955     addicon : false,
31956     editicon: false,
31957     
31958     // element that contains real text value.. (when hidden is used..)
31959      
31960     // private
31961     onRender : function(ct, position){
31962         Roo.form.Field.prototype.onRender.call(this, ct, position);
31963         
31964         if(this.store){
31965             this.store.on('beforeload', this.onBeforeLoad, this);
31966             this.store.on('load', this.onLoad, this);
31967             this.store.on('loadexception', this.onLoadException, this);
31968             this.store.load({});
31969         }
31970         
31971         
31972         
31973     },
31974
31975     // private
31976     initEvents : function(){
31977         //Roo.form.ComboBox.superclass.initEvents.call(this);
31978  
31979     },
31980
31981     onDestroy : function(){
31982        
31983         if(this.store){
31984             this.store.un('beforeload', this.onBeforeLoad, this);
31985             this.store.un('load', this.onLoad, this);
31986             this.store.un('loadexception', this.onLoadException, this);
31987         }
31988         //Roo.form.ComboBox.superclass.onDestroy.call(this);
31989     },
31990
31991     // private
31992     fireKey : function(e){
31993         if(e.isNavKeyPress() && !this.list.isVisible()){
31994             this.fireEvent("specialkey", this, e);
31995         }
31996     },
31997
31998     // private
31999     onResize: function(w, h){
32000         
32001         return; 
32002     
32003         
32004     },
32005
32006     /**
32007      * Allow or prevent the user from directly editing the field text.  If false is passed,
32008      * the user will only be able to select from the items defined in the dropdown list.  This method
32009      * is the runtime equivalent of setting the 'editable' config option at config time.
32010      * @param {Boolean} value True to allow the user to directly edit the field text
32011      */
32012     setEditable : function(value){
32013          
32014     },
32015
32016     // private
32017     onBeforeLoad : function(){
32018         
32019         Roo.log("Select before load");
32020         return;
32021     
32022         this.innerList.update(this.loadingText ?
32023                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
32024         //this.restrictHeight();
32025         this.selectedIndex = -1;
32026     },
32027
32028     // private
32029     onLoad : function(){
32030
32031     
32032         var dom = this.el.dom;
32033         dom.innerHTML = '';
32034          var od = dom.ownerDocument;
32035          
32036         if (this.emptyText) {
32037             var op = od.createElement('option');
32038             op.setAttribute('value', '');
32039             op.innerHTML = String.format('{0}', this.emptyText);
32040             dom.appendChild(op);
32041         }
32042         if(this.store.getCount() > 0){
32043            
32044             var vf = this.valueField;
32045             var df = this.displayField;
32046             this.store.data.each(function(r) {
32047                 // which colmsn to use... testing - cdoe / title..
32048                 var op = od.createElement('option');
32049                 op.setAttribute('value', r.data[vf]);
32050                 op.innerHTML = String.format('{0}', r.data[df]);
32051                 dom.appendChild(op);
32052             });
32053             if (typeof(this.defaultValue != 'undefined')) {
32054                 this.setValue(this.defaultValue);
32055             }
32056             
32057              
32058         }else{
32059             //this.onEmptyResults();
32060         }
32061         //this.el.focus();
32062     },
32063     // private
32064     onLoadException : function()
32065     {
32066         dom.innerHTML = '';
32067             
32068         Roo.log("Select on load exception");
32069         return;
32070     
32071         this.collapse();
32072         Roo.log(this.store.reader.jsonData);
32073         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
32074             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
32075         }
32076         
32077         
32078     },
32079     // private
32080     onTypeAhead : function(){
32081          
32082     },
32083
32084     // private
32085     onSelect : function(record, index){
32086         Roo.log('on select?');
32087         return;
32088         if(this.fireEvent('beforeselect', this, record, index) !== false){
32089             this.setFromData(index > -1 ? record.data : false);
32090             this.collapse();
32091             this.fireEvent('select', this, record, index);
32092         }
32093     },
32094
32095     /**
32096      * Returns the currently selected field value or empty string if no value is set.
32097      * @return {String} value The selected value
32098      */
32099     getValue : function(){
32100         var dom = this.el.dom;
32101         this.value = dom.options[dom.selectedIndex].value;
32102         return this.value;
32103         
32104     },
32105
32106     /**
32107      * Clears any text/value currently set in the field
32108      */
32109     clearValue : function(){
32110         this.value = '';
32111         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
32112         
32113     },
32114
32115     /**
32116      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
32117      * will be displayed in the field.  If the value does not match the data value of an existing item,
32118      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
32119      * Otherwise the field will be blank (although the value will still be set).
32120      * @param {String} value The value to match
32121      */
32122     setValue : function(v){
32123         var d = this.el.dom;
32124         for (var i =0; i < d.options.length;i++) {
32125             if (v == d.options[i].value) {
32126                 d.selectedIndex = i;
32127                 this.value = v;
32128                 return;
32129             }
32130         }
32131         this.clearValue();
32132     },
32133     /**
32134      * @property {Object} the last set data for the element
32135      */
32136     
32137     lastData : false,
32138     /**
32139      * Sets the value of the field based on a object which is related to the record format for the store.
32140      * @param {Object} value the value to set as. or false on reset?
32141      */
32142     setFromData : function(o){
32143         Roo.log('setfrom data?');
32144          
32145         
32146         
32147     },
32148     // private
32149     reset : function(){
32150         this.clearValue();
32151     },
32152     // private
32153     findRecord : function(prop, value){
32154         
32155         return false;
32156     
32157         var record;
32158         if(this.store.getCount() > 0){
32159             this.store.each(function(r){
32160                 if(r.data[prop] == value){
32161                     record = r;
32162                     return false;
32163                 }
32164                 return true;
32165             });
32166         }
32167         return record;
32168     },
32169     
32170     getName: function()
32171     {
32172         // returns hidden if it's set..
32173         if (!this.rendered) {return ''};
32174         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
32175         
32176     },
32177      
32178
32179     
32180
32181     // private
32182     onEmptyResults : function(){
32183         Roo.log('empty results');
32184         //this.collapse();
32185     },
32186
32187     /**
32188      * Returns true if the dropdown list is expanded, else false.
32189      */
32190     isExpanded : function(){
32191         return false;
32192     },
32193
32194     /**
32195      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
32196      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32197      * @param {String} value The data value of the item to select
32198      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32199      * selected item if it is not currently in view (defaults to true)
32200      * @return {Boolean} True if the value matched an item in the list, else false
32201      */
32202     selectByValue : function(v, scrollIntoView){
32203         Roo.log('select By Value');
32204         return false;
32205     
32206         if(v !== undefined && v !== null){
32207             var r = this.findRecord(this.valueField || this.displayField, v);
32208             if(r){
32209                 this.select(this.store.indexOf(r), scrollIntoView);
32210                 return true;
32211             }
32212         }
32213         return false;
32214     },
32215
32216     /**
32217      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
32218      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32219      * @param {Number} index The zero-based index of the list item to select
32220      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32221      * selected item if it is not currently in view (defaults to true)
32222      */
32223     select : function(index, scrollIntoView){
32224         Roo.log('select ');
32225         return  ;
32226         
32227         this.selectedIndex = index;
32228         this.view.select(index);
32229         if(scrollIntoView !== false){
32230             var el = this.view.getNode(index);
32231             if(el){
32232                 this.innerList.scrollChildIntoView(el, false);
32233             }
32234         }
32235     },
32236
32237       
32238
32239     // private
32240     validateBlur : function(){
32241         
32242         return;
32243         
32244     },
32245
32246     // private
32247     initQuery : function(){
32248         this.doQuery(this.getRawValue());
32249     },
32250
32251     // private
32252     doForce : function(){
32253         if(this.el.dom.value.length > 0){
32254             this.el.dom.value =
32255                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
32256              
32257         }
32258     },
32259
32260     /**
32261      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
32262      * query allowing the query action to be canceled if needed.
32263      * @param {String} query The SQL query to execute
32264      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
32265      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
32266      * saved in the current store (defaults to false)
32267      */
32268     doQuery : function(q, forceAll){
32269         
32270         Roo.log('doQuery?');
32271         if(q === undefined || q === null){
32272             q = '';
32273         }
32274         var qe = {
32275             query: q,
32276             forceAll: forceAll,
32277             combo: this,
32278             cancel:false
32279         };
32280         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
32281             return false;
32282         }
32283         q = qe.query;
32284         forceAll = qe.forceAll;
32285         if(forceAll === true || (q.length >= this.minChars)){
32286             if(this.lastQuery != q || this.alwaysQuery){
32287                 this.lastQuery = q;
32288                 if(this.mode == 'local'){
32289                     this.selectedIndex = -1;
32290                     if(forceAll){
32291                         this.store.clearFilter();
32292                     }else{
32293                         this.store.filter(this.displayField, q);
32294                     }
32295                     this.onLoad();
32296                 }else{
32297                     this.store.baseParams[this.queryParam] = q;
32298                     this.store.load({
32299                         params: this.getParams(q)
32300                     });
32301                     this.expand();
32302                 }
32303             }else{
32304                 this.selectedIndex = -1;
32305                 this.onLoad();   
32306             }
32307         }
32308     },
32309
32310     // private
32311     getParams : function(q){
32312         var p = {};
32313         //p[this.queryParam] = q;
32314         if(this.pageSize){
32315             p.start = 0;
32316             p.limit = this.pageSize;
32317         }
32318         return p;
32319     },
32320
32321     /**
32322      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
32323      */
32324     collapse : function(){
32325         
32326     },
32327
32328     // private
32329     collapseIf : function(e){
32330         
32331     },
32332
32333     /**
32334      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
32335      */
32336     expand : function(){
32337         
32338     } ,
32339
32340     // private
32341      
32342
32343     /** 
32344     * @cfg {Boolean} grow 
32345     * @hide 
32346     */
32347     /** 
32348     * @cfg {Number} growMin 
32349     * @hide 
32350     */
32351     /** 
32352     * @cfg {Number} growMax 
32353     * @hide 
32354     */
32355     /**
32356      * @hide
32357      * @method autoSize
32358      */
32359     
32360     setWidth : function()
32361     {
32362         
32363     },
32364     getResizeEl : function(){
32365         return this.el;
32366     }
32367 });//<script type="text/javasscript">
32368  
32369
32370 /**
32371  * @class Roo.DDView
32372  * A DnD enabled version of Roo.View.
32373  * @param {Element/String} container The Element in which to create the View.
32374  * @param {String} tpl The template string used to create the markup for each element of the View
32375  * @param {Object} config The configuration properties. These include all the config options of
32376  * {@link Roo.View} plus some specific to this class.<br>
32377  * <p>
32378  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
32379  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
32380  * <p>
32381  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
32382 .x-view-drag-insert-above {
32383         border-top:1px dotted #3366cc;
32384 }
32385 .x-view-drag-insert-below {
32386         border-bottom:1px dotted #3366cc;
32387 }
32388 </code></pre>
32389  * 
32390  */
32391  
32392 Roo.DDView = function(container, tpl, config) {
32393     Roo.DDView.superclass.constructor.apply(this, arguments);
32394     this.getEl().setStyle("outline", "0px none");
32395     this.getEl().unselectable();
32396     if (this.dragGroup) {
32397                 this.setDraggable(this.dragGroup.split(","));
32398     }
32399     if (this.dropGroup) {
32400                 this.setDroppable(this.dropGroup.split(","));
32401     }
32402     if (this.deletable) {
32403         this.setDeletable();
32404     }
32405     this.isDirtyFlag = false;
32406         this.addEvents({
32407                 "drop" : true
32408         });
32409 };
32410
32411 Roo.extend(Roo.DDView, Roo.View, {
32412 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
32413 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
32414 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
32415 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
32416
32417         isFormField: true,
32418
32419         reset: Roo.emptyFn,
32420         
32421         clearInvalid: Roo.form.Field.prototype.clearInvalid,
32422
32423         validate: function() {
32424                 return true;
32425         },
32426         
32427         destroy: function() {
32428                 this.purgeListeners();
32429                 this.getEl.removeAllListeners();
32430                 this.getEl().remove();
32431                 if (this.dragZone) {
32432                         if (this.dragZone.destroy) {
32433                                 this.dragZone.destroy();
32434                         }
32435                 }
32436                 if (this.dropZone) {
32437                         if (this.dropZone.destroy) {
32438                                 this.dropZone.destroy();
32439                         }
32440                 }
32441         },
32442
32443 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
32444         getName: function() {
32445                 return this.name;
32446         },
32447
32448 /**     Loads the View from a JSON string representing the Records to put into the Store. */
32449         setValue: function(v) {
32450                 if (!this.store) {
32451                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
32452                 }
32453                 var data = {};
32454                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
32455                 this.store.proxy = new Roo.data.MemoryProxy(data);
32456                 this.store.load();
32457         },
32458
32459 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
32460         getValue: function() {
32461                 var result = '(';
32462                 this.store.each(function(rec) {
32463                         result += rec.id + ',';
32464                 });
32465                 return result.substr(0, result.length - 1) + ')';
32466         },
32467         
32468         getIds: function() {
32469                 var i = 0, result = new Array(this.store.getCount());
32470                 this.store.each(function(rec) {
32471                         result[i++] = rec.id;
32472                 });
32473                 return result;
32474         },
32475         
32476         isDirty: function() {
32477                 return this.isDirtyFlag;
32478         },
32479
32480 /**
32481  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
32482  *      whole Element becomes the target, and this causes the drop gesture to append.
32483  */
32484     getTargetFromEvent : function(e) {
32485                 var target = e.getTarget();
32486                 while ((target !== null) && (target.parentNode != this.el.dom)) {
32487                 target = target.parentNode;
32488                 }
32489                 if (!target) {
32490                         target = this.el.dom.lastChild || this.el.dom;
32491                 }
32492                 return target;
32493     },
32494
32495 /**
32496  *      Create the drag data which consists of an object which has the property "ddel" as
32497  *      the drag proxy element. 
32498  */
32499     getDragData : function(e) {
32500         var target = this.findItemFromChild(e.getTarget());
32501                 if(target) {
32502                         this.handleSelection(e);
32503                         var selNodes = this.getSelectedNodes();
32504             var dragData = {
32505                 source: this,
32506                 copy: this.copy || (this.allowCopy && e.ctrlKey),
32507                 nodes: selNodes,
32508                 records: []
32509                         };
32510                         var selectedIndices = this.getSelectedIndexes();
32511                         for (var i = 0; i < selectedIndices.length; i++) {
32512                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
32513                         }
32514                         if (selNodes.length == 1) {
32515                                 dragData.ddel = target.cloneNode(true); // the div element
32516                         } else {
32517                                 var div = document.createElement('div'); // create the multi element drag "ghost"
32518                                 div.className = 'multi-proxy';
32519                                 for (var i = 0, len = selNodes.length; i < len; i++) {
32520                                         div.appendChild(selNodes[i].cloneNode(true));
32521                                 }
32522                                 dragData.ddel = div;
32523                         }
32524             //console.log(dragData)
32525             //console.log(dragData.ddel.innerHTML)
32526                         return dragData;
32527                 }
32528         //console.log('nodragData')
32529                 return false;
32530     },
32531     
32532 /**     Specify to which ddGroup items in this DDView may be dragged. */
32533     setDraggable: function(ddGroup) {
32534         if (ddGroup instanceof Array) {
32535                 Roo.each(ddGroup, this.setDraggable, this);
32536                 return;
32537         }
32538         if (this.dragZone) {
32539                 this.dragZone.addToGroup(ddGroup);
32540         } else {
32541                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
32542                                 containerScroll: true,
32543                                 ddGroup: ddGroup 
32544
32545                         });
32546 //                      Draggability implies selection. DragZone's mousedown selects the element.
32547                         if (!this.multiSelect) { this.singleSelect = true; }
32548
32549 //                      Wire the DragZone's handlers up to methods in *this*
32550                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
32551                 }
32552     },
32553
32554 /**     Specify from which ddGroup this DDView accepts drops. */
32555     setDroppable: function(ddGroup) {
32556         if (ddGroup instanceof Array) {
32557                 Roo.each(ddGroup, this.setDroppable, this);
32558                 return;
32559         }
32560         if (this.dropZone) {
32561                 this.dropZone.addToGroup(ddGroup);
32562         } else {
32563                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
32564                                 containerScroll: true,
32565                                 ddGroup: ddGroup
32566                         });
32567
32568 //                      Wire the DropZone's handlers up to methods in *this*
32569                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
32570                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
32571                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
32572                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
32573                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
32574                 }
32575     },
32576
32577 /**     Decide whether to drop above or below a View node. */
32578     getDropPoint : function(e, n, dd){
32579         if (n == this.el.dom) { return "above"; }
32580                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
32581                 var c = t + (b - t) / 2;
32582                 var y = Roo.lib.Event.getPageY(e);
32583                 if(y <= c) {
32584                         return "above";
32585                 }else{
32586                         return "below";
32587                 }
32588     },
32589
32590     onNodeEnter : function(n, dd, e, data){
32591                 return false;
32592     },
32593     
32594     onNodeOver : function(n, dd, e, data){
32595                 var pt = this.getDropPoint(e, n, dd);
32596                 // set the insert point style on the target node
32597                 var dragElClass = this.dropNotAllowed;
32598                 if (pt) {
32599                         var targetElClass;
32600                         if (pt == "above"){
32601                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
32602                                 targetElClass = "x-view-drag-insert-above";
32603                         } else {
32604                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
32605                                 targetElClass = "x-view-drag-insert-below";
32606                         }
32607                         if (this.lastInsertClass != targetElClass){
32608                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
32609                                 this.lastInsertClass = targetElClass;
32610                         }
32611                 }
32612                 return dragElClass;
32613         },
32614
32615     onNodeOut : function(n, dd, e, data){
32616                 this.removeDropIndicators(n);
32617     },
32618
32619     onNodeDrop : function(n, dd, e, data){
32620         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
32621                 return false;
32622         }
32623         var pt = this.getDropPoint(e, n, dd);
32624                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
32625                 if (pt == "below") { insertAt++; }
32626                 for (var i = 0; i < data.records.length; i++) {
32627                         var r = data.records[i];
32628                         var dup = this.store.getById(r.id);
32629                         if (dup && (dd != this.dragZone)) {
32630                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
32631                         } else {
32632                                 if (data.copy) {
32633                                         this.store.insert(insertAt++, r.copy());
32634                                 } else {
32635                                         data.source.isDirtyFlag = true;
32636                                         r.store.remove(r);
32637                                         this.store.insert(insertAt++, r);
32638                                 }
32639                                 this.isDirtyFlag = true;
32640                         }
32641                 }
32642                 this.dragZone.cachedTarget = null;
32643                 return true;
32644     },
32645
32646     removeDropIndicators : function(n){
32647                 if(n){
32648                         Roo.fly(n).removeClass([
32649                                 "x-view-drag-insert-above",
32650                                 "x-view-drag-insert-below"]);
32651                         this.lastInsertClass = "_noclass";
32652                 }
32653     },
32654
32655 /**
32656  *      Utility method. Add a delete option to the DDView's context menu.
32657  *      @param {String} imageUrl The URL of the "delete" icon image.
32658  */
32659         setDeletable: function(imageUrl) {
32660                 if (!this.singleSelect && !this.multiSelect) {
32661                         this.singleSelect = true;
32662                 }
32663                 var c = this.getContextMenu();
32664                 this.contextMenu.on("itemclick", function(item) {
32665                         switch (item.id) {
32666                                 case "delete":
32667                                         this.remove(this.getSelectedIndexes());
32668                                         break;
32669                         }
32670                 }, this);
32671                 this.contextMenu.add({
32672                         icon: imageUrl,
32673                         id: "delete",
32674                         text: 'Delete'
32675                 });
32676         },
32677         
32678 /**     Return the context menu for this DDView. */
32679         getContextMenu: function() {
32680                 if (!this.contextMenu) {
32681 //                      Create the View's context menu
32682                         this.contextMenu = new Roo.menu.Menu({
32683                                 id: this.id + "-contextmenu"
32684                         });
32685                         this.el.on("contextmenu", this.showContextMenu, this);
32686                 }
32687                 return this.contextMenu;
32688         },
32689         
32690         disableContextMenu: function() {
32691                 if (this.contextMenu) {
32692                         this.el.un("contextmenu", this.showContextMenu, this);
32693                 }
32694         },
32695
32696         showContextMenu: function(e, item) {
32697         item = this.findItemFromChild(e.getTarget());
32698                 if (item) {
32699                         e.stopEvent();
32700                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
32701                         this.contextMenu.showAt(e.getXY());
32702             }
32703     },
32704
32705 /**
32706  *      Remove {@link Roo.data.Record}s at the specified indices.
32707  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
32708  */
32709     remove: function(selectedIndices) {
32710                 selectedIndices = [].concat(selectedIndices);
32711                 for (var i = 0; i < selectedIndices.length; i++) {
32712                         var rec = this.store.getAt(selectedIndices[i]);
32713                         this.store.remove(rec);
32714                 }
32715     },
32716
32717 /**
32718  *      Double click fires the event, but also, if this is draggable, and there is only one other
32719  *      related DropZone, it transfers the selected node.
32720  */
32721     onDblClick : function(e){
32722         var item = this.findItemFromChild(e.getTarget());
32723         if(item){
32724             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
32725                 return false;
32726             }
32727             if (this.dragGroup) {
32728                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
32729                     while (targets.indexOf(this.dropZone) > -1) {
32730                             targets.remove(this.dropZone);
32731                                 }
32732                     if (targets.length == 1) {
32733                                         this.dragZone.cachedTarget = null;
32734                         var el = Roo.get(targets[0].getEl());
32735                         var box = el.getBox(true);
32736                         targets[0].onNodeDrop(el.dom, {
32737                                 target: el.dom,
32738                                 xy: [box.x, box.y + box.height - 1]
32739                         }, null, this.getDragData(e));
32740                     }
32741                 }
32742         }
32743     },
32744     
32745     handleSelection: function(e) {
32746                 this.dragZone.cachedTarget = null;
32747         var item = this.findItemFromChild(e.getTarget());
32748         if (!item) {
32749                 this.clearSelections(true);
32750                 return;
32751         }
32752                 if (item && (this.multiSelect || this.singleSelect)){
32753                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
32754                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
32755                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
32756                                 this.unselect(item);
32757                         } else {
32758                                 this.select(item, this.multiSelect && e.ctrlKey);
32759                                 this.lastSelection = item;
32760                         }
32761                 }
32762     },
32763
32764     onItemClick : function(item, index, e){
32765                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
32766                         return false;
32767                 }
32768                 return true;
32769     },
32770
32771     unselect : function(nodeInfo, suppressEvent){
32772                 var node = this.getNode(nodeInfo);
32773                 if(node && this.isSelected(node)){
32774                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
32775                                 Roo.fly(node).removeClass(this.selectedClass);
32776                                 this.selections.remove(node);
32777                                 if(!suppressEvent){
32778                                         this.fireEvent("selectionchange", this, this.selections);
32779                                 }
32780                         }
32781                 }
32782     }
32783 });
32784 /*
32785  * Based on:
32786  * Ext JS Library 1.1.1
32787  * Copyright(c) 2006-2007, Ext JS, LLC.
32788  *
32789  * Originally Released Under LGPL - original licence link has changed is not relivant.
32790  *
32791  * Fork - LGPL
32792  * <script type="text/javascript">
32793  */
32794  
32795 /**
32796  * @class Roo.LayoutManager
32797  * @extends Roo.util.Observable
32798  * Base class for layout managers.
32799  */
32800 Roo.LayoutManager = function(container, config){
32801     Roo.LayoutManager.superclass.constructor.call(this);
32802     this.el = Roo.get(container);
32803     // ie scrollbar fix
32804     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32805         document.body.scroll = "no";
32806     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32807         this.el.position('relative');
32808     }
32809     this.id = this.el.id;
32810     this.el.addClass("x-layout-container");
32811     /** false to disable window resize monitoring @type Boolean */
32812     this.monitorWindowResize = true;
32813     this.regions = {};
32814     this.addEvents({
32815         /**
32816          * @event layout
32817          * Fires when a layout is performed. 
32818          * @param {Roo.LayoutManager} this
32819          */
32820         "layout" : true,
32821         /**
32822          * @event regionresized
32823          * Fires when the user resizes a region. 
32824          * @param {Roo.LayoutRegion} region The resized region
32825          * @param {Number} newSize The new size (width for east/west, height for north/south)
32826          */
32827         "regionresized" : true,
32828         /**
32829          * @event regioncollapsed
32830          * Fires when a region is collapsed. 
32831          * @param {Roo.LayoutRegion} region The collapsed region
32832          */
32833         "regioncollapsed" : true,
32834         /**
32835          * @event regionexpanded
32836          * Fires when a region is expanded.  
32837          * @param {Roo.LayoutRegion} region The expanded region
32838          */
32839         "regionexpanded" : true
32840     });
32841     this.updating = false;
32842     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32843 };
32844
32845 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
32846     /**
32847      * Returns true if this layout is currently being updated
32848      * @return {Boolean}
32849      */
32850     isUpdating : function(){
32851         return this.updating; 
32852     },
32853     
32854     /**
32855      * Suspend the LayoutManager from doing auto-layouts while
32856      * making multiple add or remove calls
32857      */
32858     beginUpdate : function(){
32859         this.updating = true;    
32860     },
32861     
32862     /**
32863      * Restore auto-layouts and optionally disable the manager from performing a layout
32864      * @param {Boolean} noLayout true to disable a layout update 
32865      */
32866     endUpdate : function(noLayout){
32867         this.updating = false;
32868         if(!noLayout){
32869             this.layout();
32870         }    
32871     },
32872     
32873     layout: function(){
32874         
32875     },
32876     
32877     onRegionResized : function(region, newSize){
32878         this.fireEvent("regionresized", region, newSize);
32879         this.layout();
32880     },
32881     
32882     onRegionCollapsed : function(region){
32883         this.fireEvent("regioncollapsed", region);
32884     },
32885     
32886     onRegionExpanded : function(region){
32887         this.fireEvent("regionexpanded", region);
32888     },
32889         
32890     /**
32891      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32892      * performs box-model adjustments.
32893      * @return {Object} The size as an object {width: (the width), height: (the height)}
32894      */
32895     getViewSize : function(){
32896         var size;
32897         if(this.el.dom != document.body){
32898             size = this.el.getSize();
32899         }else{
32900             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32901         }
32902         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32903         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32904         return size;
32905     },
32906     
32907     /**
32908      * Returns the Element this layout is bound to.
32909      * @return {Roo.Element}
32910      */
32911     getEl : function(){
32912         return this.el;
32913     },
32914     
32915     /**
32916      * Returns the specified region.
32917      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32918      * @return {Roo.LayoutRegion}
32919      */
32920     getRegion : function(target){
32921         return this.regions[target.toLowerCase()];
32922     },
32923     
32924     onWindowResize : function(){
32925         if(this.monitorWindowResize){
32926             this.layout();
32927         }
32928     }
32929 });/*
32930  * Based on:
32931  * Ext JS Library 1.1.1
32932  * Copyright(c) 2006-2007, Ext JS, LLC.
32933  *
32934  * Originally Released Under LGPL - original licence link has changed is not relivant.
32935  *
32936  * Fork - LGPL
32937  * <script type="text/javascript">
32938  */
32939 /**
32940  * @class Roo.BorderLayout
32941  * @extends Roo.LayoutManager
32942  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32943  * please see: <br><br>
32944  * <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>
32945  * <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>
32946  * Example:
32947  <pre><code>
32948  var layout = new Roo.BorderLayout(document.body, {
32949     north: {
32950         initialSize: 25,
32951         titlebar: false
32952     },
32953     west: {
32954         split:true,
32955         initialSize: 200,
32956         minSize: 175,
32957         maxSize: 400,
32958         titlebar: true,
32959         collapsible: true
32960     },
32961     east: {
32962         split:true,
32963         initialSize: 202,
32964         minSize: 175,
32965         maxSize: 400,
32966         titlebar: true,
32967         collapsible: true
32968     },
32969     south: {
32970         split:true,
32971         initialSize: 100,
32972         minSize: 100,
32973         maxSize: 200,
32974         titlebar: true,
32975         collapsible: true
32976     },
32977     center: {
32978         titlebar: true,
32979         autoScroll:true,
32980         resizeTabs: true,
32981         minTabWidth: 50,
32982         preferredTabWidth: 150
32983     }
32984 });
32985
32986 // shorthand
32987 var CP = Roo.ContentPanel;
32988
32989 layout.beginUpdate();
32990 layout.add("north", new CP("north", "North"));
32991 layout.add("south", new CP("south", {title: "South", closable: true}));
32992 layout.add("west", new CP("west", {title: "West"}));
32993 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
32994 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
32995 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
32996 layout.getRegion("center").showPanel("center1");
32997 layout.endUpdate();
32998 </code></pre>
32999
33000 <b>The container the layout is rendered into can be either the body element or any other element.
33001 If it is not the body element, the container needs to either be an absolute positioned element,
33002 or you will need to add "position:relative" to the css of the container.  You will also need to specify
33003 the container size if it is not the body element.</b>
33004
33005 * @constructor
33006 * Create a new BorderLayout
33007 * @param {String/HTMLElement/Element} container The container this layout is bound to
33008 * @param {Object} config Configuration options
33009  */
33010 Roo.BorderLayout = function(container, config){
33011     config = config || {};
33012     Roo.BorderLayout.superclass.constructor.call(this, container, config);
33013     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
33014     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
33015         var target = this.factory.validRegions[i];
33016         if(config[target]){
33017             this.addRegion(target, config[target]);
33018         }
33019     }
33020 };
33021
33022 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
33023     /**
33024      * Creates and adds a new region if it doesn't already exist.
33025      * @param {String} target The target region key (north, south, east, west or center).
33026      * @param {Object} config The regions config object
33027      * @return {BorderLayoutRegion} The new region
33028      */
33029     addRegion : function(target, config){
33030         if(!this.regions[target]){
33031             var r = this.factory.create(target, this, config);
33032             this.bindRegion(target, r);
33033         }
33034         return this.regions[target];
33035     },
33036
33037     // private (kinda)
33038     bindRegion : function(name, r){
33039         this.regions[name] = r;
33040         r.on("visibilitychange", this.layout, this);
33041         r.on("paneladded", this.layout, this);
33042         r.on("panelremoved", this.layout, this);
33043         r.on("invalidated", this.layout, this);
33044         r.on("resized", this.onRegionResized, this);
33045         r.on("collapsed", this.onRegionCollapsed, this);
33046         r.on("expanded", this.onRegionExpanded, this);
33047     },
33048
33049     /**
33050      * Performs a layout update.
33051      */
33052     layout : function(){
33053         if(this.updating) {
33054             return;
33055         }
33056         var size = this.getViewSize();
33057         var w = size.width;
33058         var h = size.height;
33059         var centerW = w;
33060         var centerH = h;
33061         var centerY = 0;
33062         var centerX = 0;
33063         //var x = 0, y = 0;
33064
33065         var rs = this.regions;
33066         var north = rs["north"];
33067         var south = rs["south"]; 
33068         var west = rs["west"];
33069         var east = rs["east"];
33070         var center = rs["center"];
33071         //if(this.hideOnLayout){ // not supported anymore
33072             //c.el.setStyle("display", "none");
33073         //}
33074         if(north && north.isVisible()){
33075             var b = north.getBox();
33076             var m = north.getMargins();
33077             b.width = w - (m.left+m.right);
33078             b.x = m.left;
33079             b.y = m.top;
33080             centerY = b.height + b.y + m.bottom;
33081             centerH -= centerY;
33082             north.updateBox(this.safeBox(b));
33083         }
33084         if(south && south.isVisible()){
33085             var b = south.getBox();
33086             var m = south.getMargins();
33087             b.width = w - (m.left+m.right);
33088             b.x = m.left;
33089             var totalHeight = (b.height + m.top + m.bottom);
33090             b.y = h - totalHeight + m.top;
33091             centerH -= totalHeight;
33092             south.updateBox(this.safeBox(b));
33093         }
33094         if(west && west.isVisible()){
33095             var b = west.getBox();
33096             var m = west.getMargins();
33097             b.height = centerH - (m.top+m.bottom);
33098             b.x = m.left;
33099             b.y = centerY + m.top;
33100             var totalWidth = (b.width + m.left + m.right);
33101             centerX += totalWidth;
33102             centerW -= totalWidth;
33103             west.updateBox(this.safeBox(b));
33104         }
33105         if(east && east.isVisible()){
33106             var b = east.getBox();
33107             var m = east.getMargins();
33108             b.height = centerH - (m.top+m.bottom);
33109             var totalWidth = (b.width + m.left + m.right);
33110             b.x = w - totalWidth + m.left;
33111             b.y = centerY + m.top;
33112             centerW -= totalWidth;
33113             east.updateBox(this.safeBox(b));
33114         }
33115         if(center){
33116             var m = center.getMargins();
33117             var centerBox = {
33118                 x: centerX + m.left,
33119                 y: centerY + m.top,
33120                 width: centerW - (m.left+m.right),
33121                 height: centerH - (m.top+m.bottom)
33122             };
33123             //if(this.hideOnLayout){
33124                 //center.el.setStyle("display", "block");
33125             //}
33126             center.updateBox(this.safeBox(centerBox));
33127         }
33128         this.el.repaint();
33129         this.fireEvent("layout", this);
33130     },
33131
33132     // private
33133     safeBox : function(box){
33134         box.width = Math.max(0, box.width);
33135         box.height = Math.max(0, box.height);
33136         return box;
33137     },
33138
33139     /**
33140      * Adds a ContentPanel (or subclass) to this layout.
33141      * @param {String} target The target region key (north, south, east, west or center).
33142      * @param {Roo.ContentPanel} panel The panel to add
33143      * @return {Roo.ContentPanel} The added panel
33144      */
33145     add : function(target, panel){
33146          
33147         target = target.toLowerCase();
33148         return this.regions[target].add(panel);
33149     },
33150
33151     /**
33152      * Remove a ContentPanel (or subclass) to this layout.
33153      * @param {String} target The target region key (north, south, east, west or center).
33154      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
33155      * @return {Roo.ContentPanel} The removed panel
33156      */
33157     remove : function(target, panel){
33158         target = target.toLowerCase();
33159         return this.regions[target].remove(panel);
33160     },
33161
33162     /**
33163      * Searches all regions for a panel with the specified id
33164      * @param {String} panelId
33165      * @return {Roo.ContentPanel} The panel or null if it wasn't found
33166      */
33167     findPanel : function(panelId){
33168         var rs = this.regions;
33169         for(var target in rs){
33170             if(typeof rs[target] != "function"){
33171                 var p = rs[target].getPanel(panelId);
33172                 if(p){
33173                     return p;
33174                 }
33175             }
33176         }
33177         return null;
33178     },
33179
33180     /**
33181      * Searches all regions for a panel with the specified id and activates (shows) it.
33182      * @param {String/ContentPanel} panelId The panels id or the panel itself
33183      * @return {Roo.ContentPanel} The shown panel or null
33184      */
33185     showPanel : function(panelId) {
33186       var rs = this.regions;
33187       for(var target in rs){
33188          var r = rs[target];
33189          if(typeof r != "function"){
33190             if(r.hasPanel(panelId)){
33191                return r.showPanel(panelId);
33192             }
33193          }
33194       }
33195       return null;
33196    },
33197
33198    /**
33199      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
33200      * @param {Roo.state.Provider} provider (optional) An alternate state provider
33201      */
33202     restoreState : function(provider){
33203         if(!provider){
33204             provider = Roo.state.Manager;
33205         }
33206         var sm = new Roo.LayoutStateManager();
33207         sm.init(this, provider);
33208     },
33209
33210     /**
33211      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
33212      * object should contain properties for each region to add ContentPanels to, and each property's value should be
33213      * a valid ContentPanel config object.  Example:
33214      * <pre><code>
33215 // Create the main layout
33216 var layout = new Roo.BorderLayout('main-ct', {
33217     west: {
33218         split:true,
33219         minSize: 175,
33220         titlebar: true
33221     },
33222     center: {
33223         title:'Components'
33224     }
33225 }, 'main-ct');
33226
33227 // Create and add multiple ContentPanels at once via configs
33228 layout.batchAdd({
33229    west: {
33230        id: 'source-files',
33231        autoCreate:true,
33232        title:'Ext Source Files',
33233        autoScroll:true,
33234        fitToFrame:true
33235    },
33236    center : {
33237        el: cview,
33238        autoScroll:true,
33239        fitToFrame:true,
33240        toolbar: tb,
33241        resizeEl:'cbody'
33242    }
33243 });
33244 </code></pre>
33245      * @param {Object} regions An object containing ContentPanel configs by region name
33246      */
33247     batchAdd : function(regions){
33248         this.beginUpdate();
33249         for(var rname in regions){
33250             var lr = this.regions[rname];
33251             if(lr){
33252                 this.addTypedPanels(lr, regions[rname]);
33253             }
33254         }
33255         this.endUpdate();
33256     },
33257
33258     // private
33259     addTypedPanels : function(lr, ps){
33260         if(typeof ps == 'string'){
33261             lr.add(new Roo.ContentPanel(ps));
33262         }
33263         else if(ps instanceof Array){
33264             for(var i =0, len = ps.length; i < len; i++){
33265                 this.addTypedPanels(lr, ps[i]);
33266             }
33267         }
33268         else if(!ps.events){ // raw config?
33269             var el = ps.el;
33270             delete ps.el; // prevent conflict
33271             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
33272         }
33273         else {  // panel object assumed!
33274             lr.add(ps);
33275         }
33276     },
33277     /**
33278      * Adds a xtype elements to the layout.
33279      * <pre><code>
33280
33281 layout.addxtype({
33282        xtype : 'ContentPanel',
33283        region: 'west',
33284        items: [ .... ]
33285    }
33286 );
33287
33288 layout.addxtype({
33289         xtype : 'NestedLayoutPanel',
33290         region: 'west',
33291         layout: {
33292            center: { },
33293            west: { }   
33294         },
33295         items : [ ... list of content panels or nested layout panels.. ]
33296    }
33297 );
33298 </code></pre>
33299      * @param {Object} cfg Xtype definition of item to add.
33300      */
33301     addxtype : function(cfg)
33302     {
33303         // basically accepts a pannel...
33304         // can accept a layout region..!?!?
33305         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
33306         
33307         if (!cfg.xtype.match(/Panel$/)) {
33308             return false;
33309         }
33310         var ret = false;
33311         
33312         if (typeof(cfg.region) == 'undefined') {
33313             Roo.log("Failed to add Panel, region was not set");
33314             Roo.log(cfg);
33315             return false;
33316         }
33317         var region = cfg.region;
33318         delete cfg.region;
33319         
33320           
33321         var xitems = [];
33322         if (cfg.items) {
33323             xitems = cfg.items;
33324             delete cfg.items;
33325         }
33326         var nb = false;
33327         
33328         switch(cfg.xtype) 
33329         {
33330             case 'ContentPanel':  // ContentPanel (el, cfg)
33331             case 'ScrollPanel':  // ContentPanel (el, cfg)
33332             case 'ViewPanel': 
33333                 if(cfg.autoCreate) {
33334                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33335                 } else {
33336                     var el = this.el.createChild();
33337                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
33338                 }
33339                 
33340                 this.add(region, ret);
33341                 break;
33342             
33343             
33344             case 'TreePanel': // our new panel!
33345                 cfg.el = this.el.createChild();
33346                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33347                 this.add(region, ret);
33348                 break;
33349             
33350             case 'NestedLayoutPanel': 
33351                 // create a new Layout (which is  a Border Layout...
33352                 var el = this.el.createChild();
33353                 var clayout = cfg.layout;
33354                 delete cfg.layout;
33355                 clayout.items   = clayout.items  || [];
33356                 // replace this exitems with the clayout ones..
33357                 xitems = clayout.items;
33358                  
33359                 
33360                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
33361                     cfg.background = false;
33362                 }
33363                 var layout = new Roo.BorderLayout(el, clayout);
33364                 
33365                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
33366                 //console.log('adding nested layout panel '  + cfg.toSource());
33367                 this.add(region, ret);
33368                 nb = {}; /// find first...
33369                 break;
33370                 
33371             case 'GridPanel': 
33372             
33373                 // needs grid and region
33374                 
33375                 //var el = this.getRegion(region).el.createChild();
33376                 var el = this.el.createChild();
33377                 // create the grid first...
33378                 
33379                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
33380                 delete cfg.grid;
33381                 if (region == 'center' && this.active ) {
33382                     cfg.background = false;
33383                 }
33384                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
33385                 
33386                 this.add(region, ret);
33387                 if (cfg.background) {
33388                     ret.on('activate', function(gp) {
33389                         if (!gp.grid.rendered) {
33390                             gp.grid.render();
33391                         }
33392                     });
33393                 } else {
33394                     grid.render();
33395                 }
33396                 break;
33397            
33398            
33399            
33400                 
33401                 
33402                 
33403             default:
33404                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
33405                     
33406                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33407                     this.add(region, ret);
33408                 } else {
33409                 
33410                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
33411                     return null;
33412                 }
33413                 
33414              // GridPanel (grid, cfg)
33415             
33416         }
33417         this.beginUpdate();
33418         // add children..
33419         var region = '';
33420         var abn = {};
33421         Roo.each(xitems, function(i)  {
33422             region = nb && i.region ? i.region : false;
33423             
33424             var add = ret.addxtype(i);
33425            
33426             if (region) {
33427                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
33428                 if (!i.background) {
33429                     abn[region] = nb[region] ;
33430                 }
33431             }
33432             
33433         });
33434         this.endUpdate();
33435
33436         // make the last non-background panel active..
33437         //if (nb) { Roo.log(abn); }
33438         if (nb) {
33439             
33440             for(var r in abn) {
33441                 region = this.getRegion(r);
33442                 if (region) {
33443                     // tried using nb[r], but it does not work..
33444                      
33445                     region.showPanel(abn[r]);
33446                    
33447                 }
33448             }
33449         }
33450         return ret;
33451         
33452     }
33453 });
33454
33455 /**
33456  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
33457  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
33458  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
33459  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
33460  * <pre><code>
33461 // shorthand
33462 var CP = Roo.ContentPanel;
33463
33464 var layout = Roo.BorderLayout.create({
33465     north: {
33466         initialSize: 25,
33467         titlebar: false,
33468         panels: [new CP("north", "North")]
33469     },
33470     west: {
33471         split:true,
33472         initialSize: 200,
33473         minSize: 175,
33474         maxSize: 400,
33475         titlebar: true,
33476         collapsible: true,
33477         panels: [new CP("west", {title: "West"})]
33478     },
33479     east: {
33480         split:true,
33481         initialSize: 202,
33482         minSize: 175,
33483         maxSize: 400,
33484         titlebar: true,
33485         collapsible: true,
33486         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
33487     },
33488     south: {
33489         split:true,
33490         initialSize: 100,
33491         minSize: 100,
33492         maxSize: 200,
33493         titlebar: true,
33494         collapsible: true,
33495         panels: [new CP("south", {title: "South", closable: true})]
33496     },
33497     center: {
33498         titlebar: true,
33499         autoScroll:true,
33500         resizeTabs: true,
33501         minTabWidth: 50,
33502         preferredTabWidth: 150,
33503         panels: [
33504             new CP("center1", {title: "Close Me", closable: true}),
33505             new CP("center2", {title: "Center Panel", closable: false})
33506         ]
33507     }
33508 }, document.body);
33509
33510 layout.getRegion("center").showPanel("center1");
33511 </code></pre>
33512  * @param config
33513  * @param targetEl
33514  */
33515 Roo.BorderLayout.create = function(config, targetEl){
33516     var layout = new Roo.BorderLayout(targetEl || document.body, config);
33517     layout.beginUpdate();
33518     var regions = Roo.BorderLayout.RegionFactory.validRegions;
33519     for(var j = 0, jlen = regions.length; j < jlen; j++){
33520         var lr = regions[j];
33521         if(layout.regions[lr] && config[lr].panels){
33522             var r = layout.regions[lr];
33523             var ps = config[lr].panels;
33524             layout.addTypedPanels(r, ps);
33525         }
33526     }
33527     layout.endUpdate();
33528     return layout;
33529 };
33530
33531 // private
33532 Roo.BorderLayout.RegionFactory = {
33533     // private
33534     validRegions : ["north","south","east","west","center"],
33535
33536     // private
33537     create : function(target, mgr, config){
33538         target = target.toLowerCase();
33539         if(config.lightweight || config.basic){
33540             return new Roo.BasicLayoutRegion(mgr, config, target);
33541         }
33542         switch(target){
33543             case "north":
33544                 return new Roo.NorthLayoutRegion(mgr, config);
33545             case "south":
33546                 return new Roo.SouthLayoutRegion(mgr, config);
33547             case "east":
33548                 return new Roo.EastLayoutRegion(mgr, config);
33549             case "west":
33550                 return new Roo.WestLayoutRegion(mgr, config);
33551             case "center":
33552                 return new Roo.CenterLayoutRegion(mgr, config);
33553         }
33554         throw 'Layout region "'+target+'" not supported.';
33555     }
33556 };/*
33557  * Based on:
33558  * Ext JS Library 1.1.1
33559  * Copyright(c) 2006-2007, Ext JS, LLC.
33560  *
33561  * Originally Released Under LGPL - original licence link has changed is not relivant.
33562  *
33563  * Fork - LGPL
33564  * <script type="text/javascript">
33565  */
33566  
33567 /**
33568  * @class Roo.BasicLayoutRegion
33569  * @extends Roo.util.Observable
33570  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
33571  * and does not have a titlebar, tabs or any other features. All it does is size and position 
33572  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
33573  */
33574 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
33575     this.mgr = mgr;
33576     this.position  = pos;
33577     this.events = {
33578         /**
33579          * @scope Roo.BasicLayoutRegion
33580          */
33581         
33582         /**
33583          * @event beforeremove
33584          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
33585          * @param {Roo.LayoutRegion} this
33586          * @param {Roo.ContentPanel} panel The panel
33587          * @param {Object} e The cancel event object
33588          */
33589         "beforeremove" : true,
33590         /**
33591          * @event invalidated
33592          * Fires when the layout for this region is changed.
33593          * @param {Roo.LayoutRegion} this
33594          */
33595         "invalidated" : true,
33596         /**
33597          * @event visibilitychange
33598          * Fires when this region is shown or hidden 
33599          * @param {Roo.LayoutRegion} this
33600          * @param {Boolean} visibility true or false
33601          */
33602         "visibilitychange" : true,
33603         /**
33604          * @event paneladded
33605          * Fires when a panel is added. 
33606          * @param {Roo.LayoutRegion} this
33607          * @param {Roo.ContentPanel} panel The panel
33608          */
33609         "paneladded" : true,
33610         /**
33611          * @event panelremoved
33612          * Fires when a panel is removed. 
33613          * @param {Roo.LayoutRegion} this
33614          * @param {Roo.ContentPanel} panel The panel
33615          */
33616         "panelremoved" : true,
33617         /**
33618          * @event collapsed
33619          * Fires when this region is collapsed.
33620          * @param {Roo.LayoutRegion} this
33621          */
33622         "collapsed" : true,
33623         /**
33624          * @event expanded
33625          * Fires when this region is expanded.
33626          * @param {Roo.LayoutRegion} this
33627          */
33628         "expanded" : true,
33629         /**
33630          * @event slideshow
33631          * Fires when this region is slid into view.
33632          * @param {Roo.LayoutRegion} this
33633          */
33634         "slideshow" : true,
33635         /**
33636          * @event slidehide
33637          * Fires when this region slides out of view. 
33638          * @param {Roo.LayoutRegion} this
33639          */
33640         "slidehide" : true,
33641         /**
33642          * @event panelactivated
33643          * Fires when a panel is activated. 
33644          * @param {Roo.LayoutRegion} this
33645          * @param {Roo.ContentPanel} panel The activated panel
33646          */
33647         "panelactivated" : true,
33648         /**
33649          * @event resized
33650          * Fires when the user resizes this region. 
33651          * @param {Roo.LayoutRegion} this
33652          * @param {Number} newSize The new size (width for east/west, height for north/south)
33653          */
33654         "resized" : true
33655     };
33656     /** A collection of panels in this region. @type Roo.util.MixedCollection */
33657     this.panels = new Roo.util.MixedCollection();
33658     this.panels.getKey = this.getPanelId.createDelegate(this);
33659     this.box = null;
33660     this.activePanel = null;
33661     // ensure listeners are added...
33662     
33663     if (config.listeners || config.events) {
33664         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
33665             listeners : config.listeners || {},
33666             events : config.events || {}
33667         });
33668     }
33669     
33670     if(skipConfig !== true){
33671         this.applyConfig(config);
33672     }
33673 };
33674
33675 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
33676     getPanelId : function(p){
33677         return p.getId();
33678     },
33679     
33680     applyConfig : function(config){
33681         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33682         this.config = config;
33683         
33684     },
33685     
33686     /**
33687      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
33688      * the width, for horizontal (north, south) the height.
33689      * @param {Number} newSize The new width or height
33690      */
33691     resizeTo : function(newSize){
33692         var el = this.el ? this.el :
33693                  (this.activePanel ? this.activePanel.getEl() : null);
33694         if(el){
33695             switch(this.position){
33696                 case "east":
33697                 case "west":
33698                     el.setWidth(newSize);
33699                     this.fireEvent("resized", this, newSize);
33700                 break;
33701                 case "north":
33702                 case "south":
33703                     el.setHeight(newSize);
33704                     this.fireEvent("resized", this, newSize);
33705                 break;                
33706             }
33707         }
33708     },
33709     
33710     getBox : function(){
33711         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33712     },
33713     
33714     getMargins : function(){
33715         return this.margins;
33716     },
33717     
33718     updateBox : function(box){
33719         this.box = box;
33720         var el = this.activePanel.getEl();
33721         el.dom.style.left = box.x + "px";
33722         el.dom.style.top = box.y + "px";
33723         this.activePanel.setSize(box.width, box.height);
33724     },
33725     
33726     /**
33727      * Returns the container element for this region.
33728      * @return {Roo.Element}
33729      */
33730     getEl : function(){
33731         return this.activePanel;
33732     },
33733     
33734     /**
33735      * Returns true if this region is currently visible.
33736      * @return {Boolean}
33737      */
33738     isVisible : function(){
33739         return this.activePanel ? true : false;
33740     },
33741     
33742     setActivePanel : function(panel){
33743         panel = this.getPanel(panel);
33744         if(this.activePanel && this.activePanel != panel){
33745             this.activePanel.setActiveState(false);
33746             this.activePanel.getEl().setLeftTop(-10000,-10000);
33747         }
33748         this.activePanel = panel;
33749         panel.setActiveState(true);
33750         if(this.box){
33751             panel.setSize(this.box.width, this.box.height);
33752         }
33753         this.fireEvent("panelactivated", this, panel);
33754         this.fireEvent("invalidated");
33755     },
33756     
33757     /**
33758      * Show the specified panel.
33759      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33760      * @return {Roo.ContentPanel} The shown panel or null
33761      */
33762     showPanel : function(panel){
33763         if(panel = this.getPanel(panel)){
33764             this.setActivePanel(panel);
33765         }
33766         return panel;
33767     },
33768     
33769     /**
33770      * Get the active panel for this region.
33771      * @return {Roo.ContentPanel} The active panel or null
33772      */
33773     getActivePanel : function(){
33774         return this.activePanel;
33775     },
33776     
33777     /**
33778      * Add the passed ContentPanel(s)
33779      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33780      * @return {Roo.ContentPanel} The panel added (if only one was added)
33781      */
33782     add : function(panel){
33783         if(arguments.length > 1){
33784             for(var i = 0, len = arguments.length; i < len; i++) {
33785                 this.add(arguments[i]);
33786             }
33787             return null;
33788         }
33789         if(this.hasPanel(panel)){
33790             this.showPanel(panel);
33791             return panel;
33792         }
33793         var el = panel.getEl();
33794         if(el.dom.parentNode != this.mgr.el.dom){
33795             this.mgr.el.dom.appendChild(el.dom);
33796         }
33797         if(panel.setRegion){
33798             panel.setRegion(this);
33799         }
33800         this.panels.add(panel);
33801         el.setStyle("position", "absolute");
33802         if(!panel.background){
33803             this.setActivePanel(panel);
33804             if(this.config.initialSize && this.panels.getCount()==1){
33805                 this.resizeTo(this.config.initialSize);
33806             }
33807         }
33808         this.fireEvent("paneladded", this, panel);
33809         return panel;
33810     },
33811     
33812     /**
33813      * Returns true if the panel is in this region.
33814      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33815      * @return {Boolean}
33816      */
33817     hasPanel : function(panel){
33818         if(typeof panel == "object"){ // must be panel obj
33819             panel = panel.getId();
33820         }
33821         return this.getPanel(panel) ? true : false;
33822     },
33823     
33824     /**
33825      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33826      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33827      * @param {Boolean} preservePanel Overrides the config preservePanel option
33828      * @return {Roo.ContentPanel} The panel that was removed
33829      */
33830     remove : function(panel, preservePanel){
33831         panel = this.getPanel(panel);
33832         if(!panel){
33833             return null;
33834         }
33835         var e = {};
33836         this.fireEvent("beforeremove", this, panel, e);
33837         if(e.cancel === true){
33838             return null;
33839         }
33840         var panelId = panel.getId();
33841         this.panels.removeKey(panelId);
33842         return panel;
33843     },
33844     
33845     /**
33846      * Returns the panel specified or null if it's not in this region.
33847      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33848      * @return {Roo.ContentPanel}
33849      */
33850     getPanel : function(id){
33851         if(typeof id == "object"){ // must be panel obj
33852             return id;
33853         }
33854         return this.panels.get(id);
33855     },
33856     
33857     /**
33858      * Returns this regions position (north/south/east/west/center).
33859      * @return {String} 
33860      */
33861     getPosition: function(){
33862         return this.position;    
33863     }
33864 });/*
33865  * Based on:
33866  * Ext JS Library 1.1.1
33867  * Copyright(c) 2006-2007, Ext JS, LLC.
33868  *
33869  * Originally Released Under LGPL - original licence link has changed is not relivant.
33870  *
33871  * Fork - LGPL
33872  * <script type="text/javascript">
33873  */
33874  
33875 /**
33876  * @class Roo.LayoutRegion
33877  * @extends Roo.BasicLayoutRegion
33878  * This class represents a region in a layout manager.
33879  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
33880  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
33881  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
33882  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33883  * @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})
33884  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
33885  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
33886  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
33887  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
33888  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
33889  * @cfg {String}    title           The title for the region (overrides panel titles)
33890  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
33891  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33892  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
33893  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33894  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
33895  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33896  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
33897  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
33898  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
33899  * @cfg {Boolean}   showPin         True to show a pin button
33900  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
33901  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
33902  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
33903  * @cfg {Number}    width           For East/West panels
33904  * @cfg {Number}    height          For North/South panels
33905  * @cfg {Boolean}   split           To show the splitter
33906  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
33907  */
33908 Roo.LayoutRegion = function(mgr, config, pos){
33909     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
33910     var dh = Roo.DomHelper;
33911     /** This region's container element 
33912     * @type Roo.Element */
33913     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
33914     /** This region's title element 
33915     * @type Roo.Element */
33916
33917     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
33918         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
33919         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
33920     ]}, true);
33921     this.titleEl.enableDisplayMode();
33922     /** This region's title text element 
33923     * @type HTMLElement */
33924     this.titleTextEl = this.titleEl.dom.firstChild;
33925     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33926     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
33927     this.closeBtn.enableDisplayMode();
33928     this.closeBtn.on("click", this.closeClicked, this);
33929     this.closeBtn.hide();
33930
33931     this.createBody(config);
33932     this.visible = true;
33933     this.collapsed = false;
33934
33935     if(config.hideWhenEmpty){
33936         this.hide();
33937         this.on("paneladded", this.validateVisibility, this);
33938         this.on("panelremoved", this.validateVisibility, this);
33939     }
33940     this.applyConfig(config);
33941 };
33942
33943 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
33944
33945     createBody : function(){
33946         /** This region's body element 
33947         * @type Roo.Element */
33948         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
33949     },
33950
33951     applyConfig : function(c){
33952         if(c.collapsible && this.position != "center" && !this.collapsedEl){
33953             var dh = Roo.DomHelper;
33954             if(c.titlebar !== false){
33955                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
33956                 this.collapseBtn.on("click", this.collapse, this);
33957                 this.collapseBtn.enableDisplayMode();
33958
33959                 if(c.showPin === true || this.showPin){
33960                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
33961                     this.stickBtn.enableDisplayMode();
33962                     this.stickBtn.on("click", this.expand, this);
33963                     this.stickBtn.hide();
33964                 }
33965             }
33966             /** This region's collapsed element
33967             * @type Roo.Element */
33968             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33969                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33970             ]}, true);
33971             if(c.floatable !== false){
33972                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33973                this.collapsedEl.on("click", this.collapseClick, this);
33974             }
33975
33976             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33977                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33978                    id: "message", unselectable: "on", style:{"float":"left"}});
33979                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33980              }
33981             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33982             this.expandBtn.on("click", this.expand, this);
33983         }
33984         if(this.collapseBtn){
33985             this.collapseBtn.setVisible(c.collapsible == true);
33986         }
33987         this.cmargins = c.cmargins || this.cmargins ||
33988                          (this.position == "west" || this.position == "east" ?
33989                              {top: 0, left: 2, right:2, bottom: 0} :
33990                              {top: 2, left: 0, right:0, bottom: 2});
33991         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33992         this.bottomTabs = c.tabPosition != "top";
33993         this.autoScroll = c.autoScroll || false;
33994         if(this.autoScroll){
33995             this.bodyEl.setStyle("overflow", "auto");
33996         }else{
33997             this.bodyEl.setStyle("overflow", "hidden");
33998         }
33999         //if(c.titlebar !== false){
34000             if((!c.titlebar && !c.title) || c.titlebar === false){
34001                 this.titleEl.hide();
34002             }else{
34003                 this.titleEl.show();
34004                 if(c.title){
34005                     this.titleTextEl.innerHTML = c.title;
34006                 }
34007             }
34008         //}
34009         this.duration = c.duration || .30;
34010         this.slideDuration = c.slideDuration || .45;
34011         this.config = c;
34012         if(c.collapsed){
34013             this.collapse(true);
34014         }
34015         if(c.hidden){
34016             this.hide();
34017         }
34018     },
34019     /**
34020      * Returns true if this region is currently visible.
34021      * @return {Boolean}
34022      */
34023     isVisible : function(){
34024         return this.visible;
34025     },
34026
34027     /**
34028      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
34029      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
34030      */
34031     setCollapsedTitle : function(title){
34032         title = title || "&#160;";
34033         if(this.collapsedTitleTextEl){
34034             this.collapsedTitleTextEl.innerHTML = title;
34035         }
34036     },
34037
34038     getBox : function(){
34039         var b;
34040         if(!this.collapsed){
34041             b = this.el.getBox(false, true);
34042         }else{
34043             b = this.collapsedEl.getBox(false, true);
34044         }
34045         return b;
34046     },
34047
34048     getMargins : function(){
34049         return this.collapsed ? this.cmargins : this.margins;
34050     },
34051
34052     highlight : function(){
34053         this.el.addClass("x-layout-panel-dragover");
34054     },
34055
34056     unhighlight : function(){
34057         this.el.removeClass("x-layout-panel-dragover");
34058     },
34059
34060     updateBox : function(box){
34061         this.box = box;
34062         if(!this.collapsed){
34063             this.el.dom.style.left = box.x + "px";
34064             this.el.dom.style.top = box.y + "px";
34065             this.updateBody(box.width, box.height);
34066         }else{
34067             this.collapsedEl.dom.style.left = box.x + "px";
34068             this.collapsedEl.dom.style.top = box.y + "px";
34069             this.collapsedEl.setSize(box.width, box.height);
34070         }
34071         if(this.tabs){
34072             this.tabs.autoSizeTabs();
34073         }
34074     },
34075
34076     updateBody : function(w, h){
34077         if(w !== null){
34078             this.el.setWidth(w);
34079             w -= this.el.getBorderWidth("rl");
34080             if(this.config.adjustments){
34081                 w += this.config.adjustments[0];
34082             }
34083         }
34084         if(h !== null){
34085             this.el.setHeight(h);
34086             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
34087             h -= this.el.getBorderWidth("tb");
34088             if(this.config.adjustments){
34089                 h += this.config.adjustments[1];
34090             }
34091             this.bodyEl.setHeight(h);
34092             if(this.tabs){
34093                 h = this.tabs.syncHeight(h);
34094             }
34095         }
34096         if(this.panelSize){
34097             w = w !== null ? w : this.panelSize.width;
34098             h = h !== null ? h : this.panelSize.height;
34099         }
34100         if(this.activePanel){
34101             var el = this.activePanel.getEl();
34102             w = w !== null ? w : el.getWidth();
34103             h = h !== null ? h : el.getHeight();
34104             this.panelSize = {width: w, height: h};
34105             this.activePanel.setSize(w, h);
34106         }
34107         if(Roo.isIE && this.tabs){
34108             this.tabs.el.repaint();
34109         }
34110     },
34111
34112     /**
34113      * Returns the container element for this region.
34114      * @return {Roo.Element}
34115      */
34116     getEl : function(){
34117         return this.el;
34118     },
34119
34120     /**
34121      * Hides this region.
34122      */
34123     hide : function(){
34124         if(!this.collapsed){
34125             this.el.dom.style.left = "-2000px";
34126             this.el.hide();
34127         }else{
34128             this.collapsedEl.dom.style.left = "-2000px";
34129             this.collapsedEl.hide();
34130         }
34131         this.visible = false;
34132         this.fireEvent("visibilitychange", this, false);
34133     },
34134
34135     /**
34136      * Shows this region if it was previously hidden.
34137      */
34138     show : function(){
34139         if(!this.collapsed){
34140             this.el.show();
34141         }else{
34142             this.collapsedEl.show();
34143         }
34144         this.visible = true;
34145         this.fireEvent("visibilitychange", this, true);
34146     },
34147
34148     closeClicked : function(){
34149         if(this.activePanel){
34150             this.remove(this.activePanel);
34151         }
34152     },
34153
34154     collapseClick : function(e){
34155         if(this.isSlid){
34156            e.stopPropagation();
34157            this.slideIn();
34158         }else{
34159            e.stopPropagation();
34160            this.slideOut();
34161         }
34162     },
34163
34164     /**
34165      * Collapses this region.
34166      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
34167      */
34168     collapse : function(skipAnim){
34169         if(this.collapsed) {
34170             return;
34171         }
34172         this.collapsed = true;
34173         if(this.split){
34174             this.split.el.hide();
34175         }
34176         if(this.config.animate && skipAnim !== true){
34177             this.fireEvent("invalidated", this);
34178             this.animateCollapse();
34179         }else{
34180             this.el.setLocation(-20000,-20000);
34181             this.el.hide();
34182             this.collapsedEl.show();
34183             this.fireEvent("collapsed", this);
34184             this.fireEvent("invalidated", this);
34185         }
34186     },
34187
34188     animateCollapse : function(){
34189         // overridden
34190     },
34191
34192     /**
34193      * Expands this region if it was previously collapsed.
34194      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
34195      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
34196      */
34197     expand : function(e, skipAnim){
34198         if(e) {
34199             e.stopPropagation();
34200         }
34201         if(!this.collapsed || this.el.hasActiveFx()) {
34202             return;
34203         }
34204         if(this.isSlid){
34205             this.afterSlideIn();
34206             skipAnim = true;
34207         }
34208         this.collapsed = false;
34209         if(this.config.animate && skipAnim !== true){
34210             this.animateExpand();
34211         }else{
34212             this.el.show();
34213             if(this.split){
34214                 this.split.el.show();
34215             }
34216             this.collapsedEl.setLocation(-2000,-2000);
34217             this.collapsedEl.hide();
34218             this.fireEvent("invalidated", this);
34219             this.fireEvent("expanded", this);
34220         }
34221     },
34222
34223     animateExpand : function(){
34224         // overridden
34225     },
34226
34227     initTabs : function()
34228     {
34229         this.bodyEl.setStyle("overflow", "hidden");
34230         var ts = new Roo.TabPanel(
34231                 this.bodyEl.dom,
34232                 {
34233                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
34234                     disableTooltips: this.config.disableTabTips,
34235                     toolbar : this.config.toolbar
34236                 }
34237         );
34238         if(this.config.hideTabs){
34239             ts.stripWrap.setDisplayed(false);
34240         }
34241         this.tabs = ts;
34242         ts.resizeTabs = this.config.resizeTabs === true;
34243         ts.minTabWidth = this.config.minTabWidth || 40;
34244         ts.maxTabWidth = this.config.maxTabWidth || 250;
34245         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
34246         ts.monitorResize = false;
34247         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34248         ts.bodyEl.addClass('x-layout-tabs-body');
34249         this.panels.each(this.initPanelAsTab, this);
34250     },
34251
34252     initPanelAsTab : function(panel){
34253         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
34254                     this.config.closeOnTab && panel.isClosable());
34255         if(panel.tabTip !== undefined){
34256             ti.setTooltip(panel.tabTip);
34257         }
34258         ti.on("activate", function(){
34259               this.setActivePanel(panel);
34260         }, this);
34261         if(this.config.closeOnTab){
34262             ti.on("beforeclose", function(t, e){
34263                 e.cancel = true;
34264                 this.remove(panel);
34265             }, this);
34266         }
34267         return ti;
34268     },
34269
34270     updatePanelTitle : function(panel, title){
34271         if(this.activePanel == panel){
34272             this.updateTitle(title);
34273         }
34274         if(this.tabs){
34275             var ti = this.tabs.getTab(panel.getEl().id);
34276             ti.setText(title);
34277             if(panel.tabTip !== undefined){
34278                 ti.setTooltip(panel.tabTip);
34279             }
34280         }
34281     },
34282
34283     updateTitle : function(title){
34284         if(this.titleTextEl && !this.config.title){
34285             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
34286         }
34287     },
34288
34289     setActivePanel : function(panel){
34290         panel = this.getPanel(panel);
34291         if(this.activePanel && this.activePanel != panel){
34292             this.activePanel.setActiveState(false);
34293         }
34294         this.activePanel = panel;
34295         panel.setActiveState(true);
34296         if(this.panelSize){
34297             panel.setSize(this.panelSize.width, this.panelSize.height);
34298         }
34299         if(this.closeBtn){
34300             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
34301         }
34302         this.updateTitle(panel.getTitle());
34303         if(this.tabs){
34304             this.fireEvent("invalidated", this);
34305         }
34306         this.fireEvent("panelactivated", this, panel);
34307     },
34308
34309     /**
34310      * Shows the specified panel.
34311      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
34312      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
34313      */
34314     showPanel : function(panel)
34315     {
34316         panel = this.getPanel(panel);
34317         if(panel){
34318             if(this.tabs){
34319                 var tab = this.tabs.getTab(panel.getEl().id);
34320                 if(tab.isHidden()){
34321                     this.tabs.unhideTab(tab.id);
34322                 }
34323                 tab.activate();
34324             }else{
34325                 this.setActivePanel(panel);
34326             }
34327         }
34328         return panel;
34329     },
34330
34331     /**
34332      * Get the active panel for this region.
34333      * @return {Roo.ContentPanel} The active panel or null
34334      */
34335     getActivePanel : function(){
34336         return this.activePanel;
34337     },
34338
34339     validateVisibility : function(){
34340         if(this.panels.getCount() < 1){
34341             this.updateTitle("&#160;");
34342             this.closeBtn.hide();
34343             this.hide();
34344         }else{
34345             if(!this.isVisible()){
34346                 this.show();
34347             }
34348         }
34349     },
34350
34351     /**
34352      * Adds the passed ContentPanel(s) to this region.
34353      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34354      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
34355      */
34356     add : function(panel){
34357         if(arguments.length > 1){
34358             for(var i = 0, len = arguments.length; i < len; i++) {
34359                 this.add(arguments[i]);
34360             }
34361             return null;
34362         }
34363         if(this.hasPanel(panel)){
34364             this.showPanel(panel);
34365             return panel;
34366         }
34367         panel.setRegion(this);
34368         this.panels.add(panel);
34369         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
34370             this.bodyEl.dom.appendChild(panel.getEl().dom);
34371             if(panel.background !== true){
34372                 this.setActivePanel(panel);
34373             }
34374             this.fireEvent("paneladded", this, panel);
34375             return panel;
34376         }
34377         if(!this.tabs){
34378             this.initTabs();
34379         }else{
34380             this.initPanelAsTab(panel);
34381         }
34382         if(panel.background !== true){
34383             this.tabs.activate(panel.getEl().id);
34384         }
34385         this.fireEvent("paneladded", this, panel);
34386         return panel;
34387     },
34388
34389     /**
34390      * Hides the tab for the specified panel.
34391      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34392      */
34393     hidePanel : function(panel){
34394         if(this.tabs && (panel = this.getPanel(panel))){
34395             this.tabs.hideTab(panel.getEl().id);
34396         }
34397     },
34398
34399     /**
34400      * Unhides the tab for a previously hidden panel.
34401      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34402      */
34403     unhidePanel : function(panel){
34404         if(this.tabs && (panel = this.getPanel(panel))){
34405             this.tabs.unhideTab(panel.getEl().id);
34406         }
34407     },
34408
34409     clearPanels : function(){
34410         while(this.panels.getCount() > 0){
34411              this.remove(this.panels.first());
34412         }
34413     },
34414
34415     /**
34416      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34417      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34418      * @param {Boolean} preservePanel Overrides the config preservePanel option
34419      * @return {Roo.ContentPanel} The panel that was removed
34420      */
34421     remove : function(panel, preservePanel){
34422         panel = this.getPanel(panel);
34423         if(!panel){
34424             return null;
34425         }
34426         var e = {};
34427         this.fireEvent("beforeremove", this, panel, e);
34428         if(e.cancel === true){
34429             return null;
34430         }
34431         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
34432         var panelId = panel.getId();
34433         this.panels.removeKey(panelId);
34434         if(preservePanel){
34435             document.body.appendChild(panel.getEl().dom);
34436         }
34437         if(this.tabs){
34438             this.tabs.removeTab(panel.getEl().id);
34439         }else if (!preservePanel){
34440             this.bodyEl.dom.removeChild(panel.getEl().dom);
34441         }
34442         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
34443             var p = this.panels.first();
34444             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
34445             tempEl.appendChild(p.getEl().dom);
34446             this.bodyEl.update("");
34447             this.bodyEl.dom.appendChild(p.getEl().dom);
34448             tempEl = null;
34449             this.updateTitle(p.getTitle());
34450             this.tabs = null;
34451             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34452             this.setActivePanel(p);
34453         }
34454         panel.setRegion(null);
34455         if(this.activePanel == panel){
34456             this.activePanel = null;
34457         }
34458         if(this.config.autoDestroy !== false && preservePanel !== true){
34459             try{panel.destroy();}catch(e){}
34460         }
34461         this.fireEvent("panelremoved", this, panel);
34462         return panel;
34463     },
34464
34465     /**
34466      * Returns the TabPanel component used by this region
34467      * @return {Roo.TabPanel}
34468      */
34469     getTabs : function(){
34470         return this.tabs;
34471     },
34472
34473     createTool : function(parentEl, className){
34474         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
34475             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
34476         btn.addClassOnOver("x-layout-tools-button-over");
34477         return btn;
34478     }
34479 });/*
34480  * Based on:
34481  * Ext JS Library 1.1.1
34482  * Copyright(c) 2006-2007, Ext JS, LLC.
34483  *
34484  * Originally Released Under LGPL - original licence link has changed is not relivant.
34485  *
34486  * Fork - LGPL
34487  * <script type="text/javascript">
34488  */
34489  
34490
34491
34492 /**
34493  * @class Roo.SplitLayoutRegion
34494  * @extends Roo.LayoutRegion
34495  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
34496  */
34497 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
34498     this.cursor = cursor;
34499     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
34500 };
34501
34502 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
34503     splitTip : "Drag to resize.",
34504     collapsibleSplitTip : "Drag to resize. Double click to hide.",
34505     useSplitTips : false,
34506
34507     applyConfig : function(config){
34508         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
34509         if(config.split){
34510             if(!this.split){
34511                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
34512                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
34513                 /** The SplitBar for this region 
34514                 * @type Roo.SplitBar */
34515                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
34516                 this.split.on("moved", this.onSplitMove, this);
34517                 this.split.useShim = config.useShim === true;
34518                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
34519                 if(this.useSplitTips){
34520                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
34521                 }
34522                 if(config.collapsible){
34523                     this.split.el.on("dblclick", this.collapse,  this);
34524                 }
34525             }
34526             if(typeof config.minSize != "undefined"){
34527                 this.split.minSize = config.minSize;
34528             }
34529             if(typeof config.maxSize != "undefined"){
34530                 this.split.maxSize = config.maxSize;
34531             }
34532             if(config.hideWhenEmpty || config.hidden || config.collapsed){
34533                 this.hideSplitter();
34534             }
34535         }
34536     },
34537
34538     getHMaxSize : function(){
34539          var cmax = this.config.maxSize || 10000;
34540          var center = this.mgr.getRegion("center");
34541          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
34542     },
34543
34544     getVMaxSize : function(){
34545          var cmax = this.config.maxSize || 10000;
34546          var center = this.mgr.getRegion("center");
34547          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
34548     },
34549
34550     onSplitMove : function(split, newSize){
34551         this.fireEvent("resized", this, newSize);
34552     },
34553     
34554     /** 
34555      * Returns the {@link Roo.SplitBar} for this region.
34556      * @return {Roo.SplitBar}
34557      */
34558     getSplitBar : function(){
34559         return this.split;
34560     },
34561     
34562     hide : function(){
34563         this.hideSplitter();
34564         Roo.SplitLayoutRegion.superclass.hide.call(this);
34565     },
34566
34567     hideSplitter : function(){
34568         if(this.split){
34569             this.split.el.setLocation(-2000,-2000);
34570             this.split.el.hide();
34571         }
34572     },
34573
34574     show : function(){
34575         if(this.split){
34576             this.split.el.show();
34577         }
34578         Roo.SplitLayoutRegion.superclass.show.call(this);
34579     },
34580     
34581     beforeSlide: function(){
34582         if(Roo.isGecko){// firefox overflow auto bug workaround
34583             this.bodyEl.clip();
34584             if(this.tabs) {
34585                 this.tabs.bodyEl.clip();
34586             }
34587             if(this.activePanel){
34588                 this.activePanel.getEl().clip();
34589                 
34590                 if(this.activePanel.beforeSlide){
34591                     this.activePanel.beforeSlide();
34592                 }
34593             }
34594         }
34595     },
34596     
34597     afterSlide : function(){
34598         if(Roo.isGecko){// firefox overflow auto bug workaround
34599             this.bodyEl.unclip();
34600             if(this.tabs) {
34601                 this.tabs.bodyEl.unclip();
34602             }
34603             if(this.activePanel){
34604                 this.activePanel.getEl().unclip();
34605                 if(this.activePanel.afterSlide){
34606                     this.activePanel.afterSlide();
34607                 }
34608             }
34609         }
34610     },
34611
34612     initAutoHide : function(){
34613         if(this.autoHide !== false){
34614             if(!this.autoHideHd){
34615                 var st = new Roo.util.DelayedTask(this.slideIn, this);
34616                 this.autoHideHd = {
34617                     "mouseout": function(e){
34618                         if(!e.within(this.el, true)){
34619                             st.delay(500);
34620                         }
34621                     },
34622                     "mouseover" : function(e){
34623                         st.cancel();
34624                     },
34625                     scope : this
34626                 };
34627             }
34628             this.el.on(this.autoHideHd);
34629         }
34630     },
34631
34632     clearAutoHide : function(){
34633         if(this.autoHide !== false){
34634             this.el.un("mouseout", this.autoHideHd.mouseout);
34635             this.el.un("mouseover", this.autoHideHd.mouseover);
34636         }
34637     },
34638
34639     clearMonitor : function(){
34640         Roo.get(document).un("click", this.slideInIf, this);
34641     },
34642
34643     // these names are backwards but not changed for compat
34644     slideOut : function(){
34645         if(this.isSlid || this.el.hasActiveFx()){
34646             return;
34647         }
34648         this.isSlid = true;
34649         if(this.collapseBtn){
34650             this.collapseBtn.hide();
34651         }
34652         this.closeBtnState = this.closeBtn.getStyle('display');
34653         this.closeBtn.hide();
34654         if(this.stickBtn){
34655             this.stickBtn.show();
34656         }
34657         this.el.show();
34658         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34659         this.beforeSlide();
34660         this.el.setStyle("z-index", 10001);
34661         this.el.slideIn(this.getSlideAnchor(), {
34662             callback: function(){
34663                 this.afterSlide();
34664                 this.initAutoHide();
34665                 Roo.get(document).on("click", this.slideInIf, this);
34666                 this.fireEvent("slideshow", this);
34667             },
34668             scope: this,
34669             block: true
34670         });
34671     },
34672
34673     afterSlideIn : function(){
34674         this.clearAutoHide();
34675         this.isSlid = false;
34676         this.clearMonitor();
34677         this.el.setStyle("z-index", "");
34678         if(this.collapseBtn){
34679             this.collapseBtn.show();
34680         }
34681         this.closeBtn.setStyle('display', this.closeBtnState);
34682         if(this.stickBtn){
34683             this.stickBtn.hide();
34684         }
34685         this.fireEvent("slidehide", this);
34686     },
34687
34688     slideIn : function(cb){
34689         if(!this.isSlid || this.el.hasActiveFx()){
34690             Roo.callback(cb);
34691             return;
34692         }
34693         this.isSlid = false;
34694         this.beforeSlide();
34695         this.el.slideOut(this.getSlideAnchor(), {
34696             callback: function(){
34697                 this.el.setLeftTop(-10000, -10000);
34698                 this.afterSlide();
34699                 this.afterSlideIn();
34700                 Roo.callback(cb);
34701             },
34702             scope: this,
34703             block: true
34704         });
34705     },
34706     
34707     slideInIf : function(e){
34708         if(!e.within(this.el)){
34709             this.slideIn();
34710         }
34711     },
34712
34713     animateCollapse : function(){
34714         this.beforeSlide();
34715         this.el.setStyle("z-index", 20000);
34716         var anchor = this.getSlideAnchor();
34717         this.el.slideOut(anchor, {
34718             callback : function(){
34719                 this.el.setStyle("z-index", "");
34720                 this.collapsedEl.slideIn(anchor, {duration:.3});
34721                 this.afterSlide();
34722                 this.el.setLocation(-10000,-10000);
34723                 this.el.hide();
34724                 this.fireEvent("collapsed", this);
34725             },
34726             scope: this,
34727             block: true
34728         });
34729     },
34730
34731     animateExpand : function(){
34732         this.beforeSlide();
34733         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34734         this.el.setStyle("z-index", 20000);
34735         this.collapsedEl.hide({
34736             duration:.1
34737         });
34738         this.el.slideIn(this.getSlideAnchor(), {
34739             callback : function(){
34740                 this.el.setStyle("z-index", "");
34741                 this.afterSlide();
34742                 if(this.split){
34743                     this.split.el.show();
34744                 }
34745                 this.fireEvent("invalidated", this);
34746                 this.fireEvent("expanded", this);
34747             },
34748             scope: this,
34749             block: true
34750         });
34751     },
34752
34753     anchors : {
34754         "west" : "left",
34755         "east" : "right",
34756         "north" : "top",
34757         "south" : "bottom"
34758     },
34759
34760     sanchors : {
34761         "west" : "l",
34762         "east" : "r",
34763         "north" : "t",
34764         "south" : "b"
34765     },
34766
34767     canchors : {
34768         "west" : "tl-tr",
34769         "east" : "tr-tl",
34770         "north" : "tl-bl",
34771         "south" : "bl-tl"
34772     },
34773
34774     getAnchor : function(){
34775         return this.anchors[this.position];
34776     },
34777
34778     getCollapseAnchor : function(){
34779         return this.canchors[this.position];
34780     },
34781
34782     getSlideAnchor : function(){
34783         return this.sanchors[this.position];
34784     },
34785
34786     getAlignAdj : function(){
34787         var cm = this.cmargins;
34788         switch(this.position){
34789             case "west":
34790                 return [0, 0];
34791             break;
34792             case "east":
34793                 return [0, 0];
34794             break;
34795             case "north":
34796                 return [0, 0];
34797             break;
34798             case "south":
34799                 return [0, 0];
34800             break;
34801         }
34802     },
34803
34804     getExpandAdj : function(){
34805         var c = this.collapsedEl, cm = this.cmargins;
34806         switch(this.position){
34807             case "west":
34808                 return [-(cm.right+c.getWidth()+cm.left), 0];
34809             break;
34810             case "east":
34811                 return [cm.right+c.getWidth()+cm.left, 0];
34812             break;
34813             case "north":
34814                 return [0, -(cm.top+cm.bottom+c.getHeight())];
34815             break;
34816             case "south":
34817                 return [0, cm.top+cm.bottom+c.getHeight()];
34818             break;
34819         }
34820     }
34821 });/*
34822  * Based on:
34823  * Ext JS Library 1.1.1
34824  * Copyright(c) 2006-2007, Ext JS, LLC.
34825  *
34826  * Originally Released Under LGPL - original licence link has changed is not relivant.
34827  *
34828  * Fork - LGPL
34829  * <script type="text/javascript">
34830  */
34831 /*
34832  * These classes are private internal classes
34833  */
34834 Roo.CenterLayoutRegion = function(mgr, config){
34835     Roo.LayoutRegion.call(this, mgr, config, "center");
34836     this.visible = true;
34837     this.minWidth = config.minWidth || 20;
34838     this.minHeight = config.minHeight || 20;
34839 };
34840
34841 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
34842     hide : function(){
34843         // center panel can't be hidden
34844     },
34845     
34846     show : function(){
34847         // center panel can't be hidden
34848     },
34849     
34850     getMinWidth: function(){
34851         return this.minWidth;
34852     },
34853     
34854     getMinHeight: function(){
34855         return this.minHeight;
34856     }
34857 });
34858
34859
34860 Roo.NorthLayoutRegion = function(mgr, config){
34861     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
34862     if(this.split){
34863         this.split.placement = Roo.SplitBar.TOP;
34864         this.split.orientation = Roo.SplitBar.VERTICAL;
34865         this.split.el.addClass("x-layout-split-v");
34866     }
34867     var size = config.initialSize || config.height;
34868     if(typeof size != "undefined"){
34869         this.el.setHeight(size);
34870     }
34871 };
34872 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
34873     orientation: Roo.SplitBar.VERTICAL,
34874     getBox : function(){
34875         if(this.collapsed){
34876             return this.collapsedEl.getBox();
34877         }
34878         var box = this.el.getBox();
34879         if(this.split){
34880             box.height += this.split.el.getHeight();
34881         }
34882         return box;
34883     },
34884     
34885     updateBox : function(box){
34886         if(this.split && !this.collapsed){
34887             box.height -= this.split.el.getHeight();
34888             this.split.el.setLeft(box.x);
34889             this.split.el.setTop(box.y+box.height);
34890             this.split.el.setWidth(box.width);
34891         }
34892         if(this.collapsed){
34893             this.updateBody(box.width, null);
34894         }
34895         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34896     }
34897 });
34898
34899 Roo.SouthLayoutRegion = function(mgr, config){
34900     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
34901     if(this.split){
34902         this.split.placement = Roo.SplitBar.BOTTOM;
34903         this.split.orientation = Roo.SplitBar.VERTICAL;
34904         this.split.el.addClass("x-layout-split-v");
34905     }
34906     var size = config.initialSize || config.height;
34907     if(typeof size != "undefined"){
34908         this.el.setHeight(size);
34909     }
34910 };
34911 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
34912     orientation: Roo.SplitBar.VERTICAL,
34913     getBox : function(){
34914         if(this.collapsed){
34915             return this.collapsedEl.getBox();
34916         }
34917         var box = this.el.getBox();
34918         if(this.split){
34919             var sh = this.split.el.getHeight();
34920             box.height += sh;
34921             box.y -= sh;
34922         }
34923         return box;
34924     },
34925     
34926     updateBox : function(box){
34927         if(this.split && !this.collapsed){
34928             var sh = this.split.el.getHeight();
34929             box.height -= sh;
34930             box.y += sh;
34931             this.split.el.setLeft(box.x);
34932             this.split.el.setTop(box.y-sh);
34933             this.split.el.setWidth(box.width);
34934         }
34935         if(this.collapsed){
34936             this.updateBody(box.width, null);
34937         }
34938         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34939     }
34940 });
34941
34942 Roo.EastLayoutRegion = function(mgr, config){
34943     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
34944     if(this.split){
34945         this.split.placement = Roo.SplitBar.RIGHT;
34946         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34947         this.split.el.addClass("x-layout-split-h");
34948     }
34949     var size = config.initialSize || config.width;
34950     if(typeof size != "undefined"){
34951         this.el.setWidth(size);
34952     }
34953 };
34954 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
34955     orientation: Roo.SplitBar.HORIZONTAL,
34956     getBox : function(){
34957         if(this.collapsed){
34958             return this.collapsedEl.getBox();
34959         }
34960         var box = this.el.getBox();
34961         if(this.split){
34962             var sw = this.split.el.getWidth();
34963             box.width += sw;
34964             box.x -= sw;
34965         }
34966         return box;
34967     },
34968
34969     updateBox : function(box){
34970         if(this.split && !this.collapsed){
34971             var sw = this.split.el.getWidth();
34972             box.width -= sw;
34973             this.split.el.setLeft(box.x);
34974             this.split.el.setTop(box.y);
34975             this.split.el.setHeight(box.height);
34976             box.x += sw;
34977         }
34978         if(this.collapsed){
34979             this.updateBody(null, box.height);
34980         }
34981         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34982     }
34983 });
34984
34985 Roo.WestLayoutRegion = function(mgr, config){
34986     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
34987     if(this.split){
34988         this.split.placement = Roo.SplitBar.LEFT;
34989         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34990         this.split.el.addClass("x-layout-split-h");
34991     }
34992     var size = config.initialSize || config.width;
34993     if(typeof size != "undefined"){
34994         this.el.setWidth(size);
34995     }
34996 };
34997 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
34998     orientation: Roo.SplitBar.HORIZONTAL,
34999     getBox : function(){
35000         if(this.collapsed){
35001             return this.collapsedEl.getBox();
35002         }
35003         var box = this.el.getBox();
35004         if(this.split){
35005             box.width += this.split.el.getWidth();
35006         }
35007         return box;
35008     },
35009     
35010     updateBox : function(box){
35011         if(this.split && !this.collapsed){
35012             var sw = this.split.el.getWidth();
35013             box.width -= sw;
35014             this.split.el.setLeft(box.x+box.width);
35015             this.split.el.setTop(box.y);
35016             this.split.el.setHeight(box.height);
35017         }
35018         if(this.collapsed){
35019             this.updateBody(null, box.height);
35020         }
35021         Roo.LayoutRegion.prototype.updateBox.call(this, box);
35022     }
35023 });
35024 /*
35025  * Based on:
35026  * Ext JS Library 1.1.1
35027  * Copyright(c) 2006-2007, Ext JS, LLC.
35028  *
35029  * Originally Released Under LGPL - original licence link has changed is not relivant.
35030  *
35031  * Fork - LGPL
35032  * <script type="text/javascript">
35033  */
35034  
35035  
35036 /*
35037  * Private internal class for reading and applying state
35038  */
35039 Roo.LayoutStateManager = function(layout){
35040      // default empty state
35041      this.state = {
35042         north: {},
35043         south: {},
35044         east: {},
35045         west: {}       
35046     };
35047 };
35048
35049 Roo.LayoutStateManager.prototype = {
35050     init : function(layout, provider){
35051         this.provider = provider;
35052         var state = provider.get(layout.id+"-layout-state");
35053         if(state){
35054             var wasUpdating = layout.isUpdating();
35055             if(!wasUpdating){
35056                 layout.beginUpdate();
35057             }
35058             for(var key in state){
35059                 if(typeof state[key] != "function"){
35060                     var rstate = state[key];
35061                     var r = layout.getRegion(key);
35062                     if(r && rstate){
35063                         if(rstate.size){
35064                             r.resizeTo(rstate.size);
35065                         }
35066                         if(rstate.collapsed == true){
35067                             r.collapse(true);
35068                         }else{
35069                             r.expand(null, true);
35070                         }
35071                     }
35072                 }
35073             }
35074             if(!wasUpdating){
35075                 layout.endUpdate();
35076             }
35077             this.state = state; 
35078         }
35079         this.layout = layout;
35080         layout.on("regionresized", this.onRegionResized, this);
35081         layout.on("regioncollapsed", this.onRegionCollapsed, this);
35082         layout.on("regionexpanded", this.onRegionExpanded, this);
35083     },
35084     
35085     storeState : function(){
35086         this.provider.set(this.layout.id+"-layout-state", this.state);
35087     },
35088     
35089     onRegionResized : function(region, newSize){
35090         this.state[region.getPosition()].size = newSize;
35091         this.storeState();
35092     },
35093     
35094     onRegionCollapsed : function(region){
35095         this.state[region.getPosition()].collapsed = true;
35096         this.storeState();
35097     },
35098     
35099     onRegionExpanded : function(region){
35100         this.state[region.getPosition()].collapsed = false;
35101         this.storeState();
35102     }
35103 };/*
35104  * Based on:
35105  * Ext JS Library 1.1.1
35106  * Copyright(c) 2006-2007, Ext JS, LLC.
35107  *
35108  * Originally Released Under LGPL - original licence link has changed is not relivant.
35109  *
35110  * Fork - LGPL
35111  * <script type="text/javascript">
35112  */
35113 /**
35114  * @class Roo.ContentPanel
35115  * @extends Roo.util.Observable
35116  * A basic ContentPanel element.
35117  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
35118  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
35119  * @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
35120  * @cfg {Boolean}   closable      True if the panel can be closed/removed
35121  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
35122  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
35123  * @cfg {Toolbar}   toolbar       A toolbar for this panel
35124  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
35125  * @cfg {String} title          The title for this panel
35126  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
35127  * @cfg {String} url            Calls {@link #setUrl} with this value
35128  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
35129  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
35130  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
35131  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
35132
35133  * @constructor
35134  * Create a new ContentPanel.
35135  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
35136  * @param {String/Object} config A string to set only the title or a config object
35137  * @param {String} content (optional) Set the HTML content for this panel
35138  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
35139  */
35140 Roo.ContentPanel = function(el, config, content){
35141     
35142      
35143     /*
35144     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
35145         config = el;
35146         el = Roo.id();
35147     }
35148     if (config && config.parentLayout) { 
35149         el = config.parentLayout.el.createChild(); 
35150     }
35151     */
35152     if(el.autoCreate){ // xtype is available if this is called from factory
35153         config = el;
35154         el = Roo.id();
35155     }
35156     this.el = Roo.get(el);
35157     if(!this.el && config && config.autoCreate){
35158         if(typeof config.autoCreate == "object"){
35159             if(!config.autoCreate.id){
35160                 config.autoCreate.id = config.id||el;
35161             }
35162             this.el = Roo.DomHelper.append(document.body,
35163                         config.autoCreate, true);
35164         }else{
35165             this.el = Roo.DomHelper.append(document.body,
35166                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
35167         }
35168     }
35169     this.closable = false;
35170     this.loaded = false;
35171     this.active = false;
35172     if(typeof config == "string"){
35173         this.title = config;
35174     }else{
35175         Roo.apply(this, config);
35176     }
35177     
35178     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
35179         this.wrapEl = this.el.wrap();
35180         this.toolbar.container = this.el.insertSibling(false, 'before');
35181         this.toolbar = new Roo.Toolbar(this.toolbar);
35182     }
35183     
35184     // xtype created footer. - not sure if will work as we normally have to render first..
35185     if (this.footer && !this.footer.el && this.footer.xtype) {
35186         if (!this.wrapEl) {
35187             this.wrapEl = this.el.wrap();
35188         }
35189     
35190         this.footer.container = this.wrapEl.createChild();
35191          
35192         this.footer = Roo.factory(this.footer, Roo);
35193         
35194     }
35195     
35196     if(this.resizeEl){
35197         this.resizeEl = Roo.get(this.resizeEl, true);
35198     }else{
35199         this.resizeEl = this.el;
35200     }
35201     // handle view.xtype
35202     
35203  
35204     
35205     
35206     this.addEvents({
35207         /**
35208          * @event activate
35209          * Fires when this panel is activated. 
35210          * @param {Roo.ContentPanel} this
35211          */
35212         "activate" : true,
35213         /**
35214          * @event deactivate
35215          * Fires when this panel is activated. 
35216          * @param {Roo.ContentPanel} this
35217          */
35218         "deactivate" : true,
35219
35220         /**
35221          * @event resize
35222          * Fires when this panel is resized if fitToFrame is true.
35223          * @param {Roo.ContentPanel} this
35224          * @param {Number} width The width after any component adjustments
35225          * @param {Number} height The height after any component adjustments
35226          */
35227         "resize" : true,
35228         
35229          /**
35230          * @event render
35231          * Fires when this tab is created
35232          * @param {Roo.ContentPanel} this
35233          */
35234         "render" : true
35235         
35236         
35237         
35238     });
35239     
35240
35241     
35242     
35243     if(this.autoScroll){
35244         this.resizeEl.setStyle("overflow", "auto");
35245     } else {
35246         // fix randome scrolling
35247         this.el.on('scroll', function() {
35248             Roo.log('fix random scolling');
35249             this.scrollTo('top',0); 
35250         });
35251     }
35252     content = content || this.content;
35253     if(content){
35254         this.setContent(content);
35255     }
35256     if(config && config.url){
35257         this.setUrl(this.url, this.params, this.loadOnce);
35258     }
35259     
35260     
35261     
35262     Roo.ContentPanel.superclass.constructor.call(this);
35263     
35264     if (this.view && typeof(this.view.xtype) != 'undefined') {
35265         this.view.el = this.el.appendChild(document.createElement("div"));
35266         this.view = Roo.factory(this.view); 
35267         this.view.render  &&  this.view.render(false, '');  
35268     }
35269     
35270     
35271     this.fireEvent('render', this);
35272 };
35273
35274 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
35275     tabTip:'',
35276     setRegion : function(region){
35277         this.region = region;
35278         if(region){
35279            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
35280         }else{
35281            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
35282         } 
35283     },
35284     
35285     /**
35286      * Returns the toolbar for this Panel if one was configured. 
35287      * @return {Roo.Toolbar} 
35288      */
35289     getToolbar : function(){
35290         return this.toolbar;
35291     },
35292     
35293     setActiveState : function(active){
35294         this.active = active;
35295         if(!active){
35296             this.fireEvent("deactivate", this);
35297         }else{
35298             this.fireEvent("activate", this);
35299         }
35300     },
35301     /**
35302      * Updates this panel's element
35303      * @param {String} content The new content
35304      * @param {Boolean} loadScripts (optional) true to look for and process scripts
35305     */
35306     setContent : function(content, loadScripts){
35307         this.el.update(content, loadScripts);
35308     },
35309
35310     ignoreResize : function(w, h){
35311         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
35312             return true;
35313         }else{
35314             this.lastSize = {width: w, height: h};
35315             return false;
35316         }
35317     },
35318     /**
35319      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
35320      * @return {Roo.UpdateManager} The UpdateManager
35321      */
35322     getUpdateManager : function(){
35323         return this.el.getUpdateManager();
35324     },
35325      /**
35326      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
35327      * @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:
35328 <pre><code>
35329 panel.load({
35330     url: "your-url.php",
35331     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
35332     callback: yourFunction,
35333     scope: yourObject, //(optional scope)
35334     discardUrl: false,
35335     nocache: false,
35336     text: "Loading...",
35337     timeout: 30,
35338     scripts: false
35339 });
35340 </code></pre>
35341      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
35342      * 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.
35343      * @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}
35344      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
35345      * @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.
35346      * @return {Roo.ContentPanel} this
35347      */
35348     load : function(){
35349         var um = this.el.getUpdateManager();
35350         um.update.apply(um, arguments);
35351         return this;
35352     },
35353
35354
35355     /**
35356      * 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.
35357      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
35358      * @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)
35359      * @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)
35360      * @return {Roo.UpdateManager} The UpdateManager
35361      */
35362     setUrl : function(url, params, loadOnce){
35363         if(this.refreshDelegate){
35364             this.removeListener("activate", this.refreshDelegate);
35365         }
35366         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35367         this.on("activate", this.refreshDelegate);
35368         return this.el.getUpdateManager();
35369     },
35370     
35371     _handleRefresh : function(url, params, loadOnce){
35372         if(!loadOnce || !this.loaded){
35373             var updater = this.el.getUpdateManager();
35374             updater.update(url, params, this._setLoaded.createDelegate(this));
35375         }
35376     },
35377     
35378     _setLoaded : function(){
35379         this.loaded = true;
35380     }, 
35381     
35382     /**
35383      * Returns this panel's id
35384      * @return {String} 
35385      */
35386     getId : function(){
35387         return this.el.id;
35388     },
35389     
35390     /** 
35391      * Returns this panel's element - used by regiosn to add.
35392      * @return {Roo.Element} 
35393      */
35394     getEl : function(){
35395         return this.wrapEl || this.el;
35396     },
35397     
35398     adjustForComponents : function(width, height)
35399     {
35400         //Roo.log('adjustForComponents ');
35401         if(this.resizeEl != this.el){
35402             width -= this.el.getFrameWidth('lr');
35403             height -= this.el.getFrameWidth('tb');
35404         }
35405         if(this.toolbar){
35406             var te = this.toolbar.getEl();
35407             height -= te.getHeight();
35408             te.setWidth(width);
35409         }
35410         if(this.footer){
35411             var te = this.footer.getEl();
35412             Roo.log("footer:" + te.getHeight());
35413             
35414             height -= te.getHeight();
35415             te.setWidth(width);
35416         }
35417         
35418         
35419         if(this.adjustments){
35420             width += this.adjustments[0];
35421             height += this.adjustments[1];
35422         }
35423         return {"width": width, "height": height};
35424     },
35425     
35426     setSize : function(width, height){
35427         if(this.fitToFrame && !this.ignoreResize(width, height)){
35428             if(this.fitContainer && this.resizeEl != this.el){
35429                 this.el.setSize(width, height);
35430             }
35431             var size = this.adjustForComponents(width, height);
35432             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
35433             this.fireEvent('resize', this, size.width, size.height);
35434         }
35435     },
35436     
35437     /**
35438      * Returns this panel's title
35439      * @return {String} 
35440      */
35441     getTitle : function(){
35442         return this.title;
35443     },
35444     
35445     /**
35446      * Set this panel's title
35447      * @param {String} title
35448      */
35449     setTitle : function(title){
35450         this.title = title;
35451         if(this.region){
35452             this.region.updatePanelTitle(this, title);
35453         }
35454     },
35455     
35456     /**
35457      * Returns true is this panel was configured to be closable
35458      * @return {Boolean} 
35459      */
35460     isClosable : function(){
35461         return this.closable;
35462     },
35463     
35464     beforeSlide : function(){
35465         this.el.clip();
35466         this.resizeEl.clip();
35467     },
35468     
35469     afterSlide : function(){
35470         this.el.unclip();
35471         this.resizeEl.unclip();
35472     },
35473     
35474     /**
35475      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
35476      *   Will fail silently if the {@link #setUrl} method has not been called.
35477      *   This does not activate the panel, just updates its content.
35478      */
35479     refresh : function(){
35480         if(this.refreshDelegate){
35481            this.loaded = false;
35482            this.refreshDelegate();
35483         }
35484     },
35485     
35486     /**
35487      * Destroys this panel
35488      */
35489     destroy : function(){
35490         this.el.removeAllListeners();
35491         var tempEl = document.createElement("span");
35492         tempEl.appendChild(this.el.dom);
35493         tempEl.innerHTML = "";
35494         this.el.remove();
35495         this.el = null;
35496     },
35497     
35498     /**
35499      * form - if the content panel contains a form - this is a reference to it.
35500      * @type {Roo.form.Form}
35501      */
35502     form : false,
35503     /**
35504      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
35505      *    This contains a reference to it.
35506      * @type {Roo.View}
35507      */
35508     view : false,
35509     
35510       /**
35511      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
35512      * <pre><code>
35513
35514 layout.addxtype({
35515        xtype : 'Form',
35516        items: [ .... ]
35517    }
35518 );
35519
35520 </code></pre>
35521      * @param {Object} cfg Xtype definition of item to add.
35522      */
35523     
35524     addxtype : function(cfg) {
35525         // add form..
35526         if (cfg.xtype.match(/^Form$/)) {
35527             
35528             var el;
35529             //if (this.footer) {
35530             //    el = this.footer.container.insertSibling(false, 'before');
35531             //} else {
35532                 el = this.el.createChild();
35533             //}
35534
35535             this.form = new  Roo.form.Form(cfg);
35536             
35537             
35538             if ( this.form.allItems.length) {
35539                 this.form.render(el.dom);
35540             }
35541             return this.form;
35542         }
35543         // should only have one of theses..
35544         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
35545             // views.. should not be just added - used named prop 'view''
35546             
35547             cfg.el = this.el.appendChild(document.createElement("div"));
35548             // factory?
35549             
35550             var ret = new Roo.factory(cfg);
35551              
35552              ret.render && ret.render(false, ''); // render blank..
35553             this.view = ret;
35554             return ret;
35555         }
35556         return false;
35557     }
35558 });
35559
35560 /**
35561  * @class Roo.GridPanel
35562  * @extends Roo.ContentPanel
35563  * @constructor
35564  * Create a new GridPanel.
35565  * @param {Roo.grid.Grid} grid The grid for this panel
35566  * @param {String/Object} config A string to set only the panel's title, or a config object
35567  */
35568 Roo.GridPanel = function(grid, config){
35569     
35570   
35571     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
35572         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
35573         
35574     this.wrapper.dom.appendChild(grid.getGridEl().dom);
35575     
35576     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
35577     
35578     if(this.toolbar){
35579         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
35580     }
35581     // xtype created footer. - not sure if will work as we normally have to render first..
35582     if (this.footer && !this.footer.el && this.footer.xtype) {
35583         
35584         this.footer.container = this.grid.getView().getFooterPanel(true);
35585         this.footer.dataSource = this.grid.dataSource;
35586         this.footer = Roo.factory(this.footer, Roo);
35587         
35588     }
35589     
35590     grid.monitorWindowResize = false; // turn off autosizing
35591     grid.autoHeight = false;
35592     grid.autoWidth = false;
35593     this.grid = grid;
35594     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
35595 };
35596
35597 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
35598     getId : function(){
35599         return this.grid.id;
35600     },
35601     
35602     /**
35603      * Returns the grid for this panel
35604      * @return {Roo.grid.Grid} 
35605      */
35606     getGrid : function(){
35607         return this.grid;    
35608     },
35609     
35610     setSize : function(width, height){
35611         if(!this.ignoreResize(width, height)){
35612             var grid = this.grid;
35613             var size = this.adjustForComponents(width, height);
35614             grid.getGridEl().setSize(size.width, size.height);
35615             grid.autoSize();
35616         }
35617     },
35618     
35619     beforeSlide : function(){
35620         this.grid.getView().scroller.clip();
35621     },
35622     
35623     afterSlide : function(){
35624         this.grid.getView().scroller.unclip();
35625     },
35626     
35627     destroy : function(){
35628         this.grid.destroy();
35629         delete this.grid;
35630         Roo.GridPanel.superclass.destroy.call(this); 
35631     }
35632 });
35633
35634
35635 /**
35636  * @class Roo.NestedLayoutPanel
35637  * @extends Roo.ContentPanel
35638  * @constructor
35639  * Create a new NestedLayoutPanel.
35640  * 
35641  * 
35642  * @param {Roo.BorderLayout} layout The layout for this panel
35643  * @param {String/Object} config A string to set only the title or a config object
35644  */
35645 Roo.NestedLayoutPanel = function(layout, config)
35646 {
35647     // construct with only one argument..
35648     /* FIXME - implement nicer consturctors
35649     if (layout.layout) {
35650         config = layout;
35651         layout = config.layout;
35652         delete config.layout;
35653     }
35654     if (layout.xtype && !layout.getEl) {
35655         // then layout needs constructing..
35656         layout = Roo.factory(layout, Roo);
35657     }
35658     */
35659     
35660     
35661     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
35662     
35663     layout.monitorWindowResize = false; // turn off autosizing
35664     this.layout = layout;
35665     this.layout.getEl().addClass("x-layout-nested-layout");
35666     
35667     
35668     
35669     
35670 };
35671
35672 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
35673
35674     setSize : function(width, height){
35675         if(!this.ignoreResize(width, height)){
35676             var size = this.adjustForComponents(width, height);
35677             var el = this.layout.getEl();
35678             el.setSize(size.width, size.height);
35679             var touch = el.dom.offsetWidth;
35680             this.layout.layout();
35681             // ie requires a double layout on the first pass
35682             if(Roo.isIE && !this.initialized){
35683                 this.initialized = true;
35684                 this.layout.layout();
35685             }
35686         }
35687     },
35688     
35689     // activate all subpanels if not currently active..
35690     
35691     setActiveState : function(active){
35692         this.active = active;
35693         if(!active){
35694             this.fireEvent("deactivate", this);
35695             return;
35696         }
35697         
35698         this.fireEvent("activate", this);
35699         // not sure if this should happen before or after..
35700         if (!this.layout) {
35701             return; // should not happen..
35702         }
35703         var reg = false;
35704         for (var r in this.layout.regions) {
35705             reg = this.layout.getRegion(r);
35706             if (reg.getActivePanel()) {
35707                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
35708                 reg.setActivePanel(reg.getActivePanel());
35709                 continue;
35710             }
35711             if (!reg.panels.length) {
35712                 continue;
35713             }
35714             reg.showPanel(reg.getPanel(0));
35715         }
35716         
35717         
35718         
35719         
35720     },
35721     
35722     /**
35723      * Returns the nested BorderLayout for this panel
35724      * @return {Roo.BorderLayout} 
35725      */
35726     getLayout : function(){
35727         return this.layout;
35728     },
35729     
35730      /**
35731      * Adds a xtype elements to the layout of the nested panel
35732      * <pre><code>
35733
35734 panel.addxtype({
35735        xtype : 'ContentPanel',
35736        region: 'west',
35737        items: [ .... ]
35738    }
35739 );
35740
35741 panel.addxtype({
35742         xtype : 'NestedLayoutPanel',
35743         region: 'west',
35744         layout: {
35745            center: { },
35746            west: { }   
35747         },
35748         items : [ ... list of content panels or nested layout panels.. ]
35749    }
35750 );
35751 </code></pre>
35752      * @param {Object} cfg Xtype definition of item to add.
35753      */
35754     addxtype : function(cfg) {
35755         return this.layout.addxtype(cfg);
35756     
35757     }
35758 });
35759
35760 Roo.ScrollPanel = function(el, config, content){
35761     config = config || {};
35762     config.fitToFrame = true;
35763     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
35764     
35765     this.el.dom.style.overflow = "hidden";
35766     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
35767     this.el.removeClass("x-layout-inactive-content");
35768     this.el.on("mousewheel", this.onWheel, this);
35769
35770     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
35771     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
35772     up.unselectable(); down.unselectable();
35773     up.on("click", this.scrollUp, this);
35774     down.on("click", this.scrollDown, this);
35775     up.addClassOnOver("x-scroller-btn-over");
35776     down.addClassOnOver("x-scroller-btn-over");
35777     up.addClassOnClick("x-scroller-btn-click");
35778     down.addClassOnClick("x-scroller-btn-click");
35779     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
35780
35781     this.resizeEl = this.el;
35782     this.el = wrap; this.up = up; this.down = down;
35783 };
35784
35785 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
35786     increment : 100,
35787     wheelIncrement : 5,
35788     scrollUp : function(){
35789         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
35790     },
35791
35792     scrollDown : function(){
35793         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
35794     },
35795
35796     afterScroll : function(){
35797         var el = this.resizeEl;
35798         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
35799         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35800         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35801     },
35802
35803     setSize : function(){
35804         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
35805         this.afterScroll();
35806     },
35807
35808     onWheel : function(e){
35809         var d = e.getWheelDelta();
35810         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
35811         this.afterScroll();
35812         e.stopEvent();
35813     },
35814
35815     setContent : function(content, loadScripts){
35816         this.resizeEl.update(content, loadScripts);
35817     }
35818
35819 });
35820
35821
35822
35823
35824
35825
35826
35827
35828
35829 /**
35830  * @class Roo.TreePanel
35831  * @extends Roo.ContentPanel
35832  * @constructor
35833  * Create a new TreePanel. - defaults to fit/scoll contents.
35834  * @param {String/Object} config A string to set only the panel's title, or a config object
35835  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
35836  */
35837 Roo.TreePanel = function(config){
35838     var el = config.el;
35839     var tree = config.tree;
35840     delete config.tree; 
35841     delete config.el; // hopefull!
35842     
35843     // wrapper for IE7 strict & safari scroll issue
35844     
35845     var treeEl = el.createChild();
35846     config.resizeEl = treeEl;
35847     
35848     
35849     
35850     Roo.TreePanel.superclass.constructor.call(this, el, config);
35851  
35852  
35853     this.tree = new Roo.tree.TreePanel(treeEl , tree);
35854     //console.log(tree);
35855     this.on('activate', function()
35856     {
35857         if (this.tree.rendered) {
35858             return;
35859         }
35860         //console.log('render tree');
35861         this.tree.render();
35862     });
35863     // this should not be needed.. - it's actually the 'el' that resizes?
35864     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
35865     
35866     //this.on('resize',  function (cp, w, h) {
35867     //        this.tree.innerCt.setWidth(w);
35868     //        this.tree.innerCt.setHeight(h);
35869     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
35870     //});
35871
35872         
35873     
35874 };
35875
35876 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
35877     fitToFrame : true,
35878     autoScroll : true
35879 });
35880
35881
35882
35883
35884
35885
35886
35887
35888
35889
35890
35891 /*
35892  * Based on:
35893  * Ext JS Library 1.1.1
35894  * Copyright(c) 2006-2007, Ext JS, LLC.
35895  *
35896  * Originally Released Under LGPL - original licence link has changed is not relivant.
35897  *
35898  * Fork - LGPL
35899  * <script type="text/javascript">
35900  */
35901  
35902
35903 /**
35904  * @class Roo.ReaderLayout
35905  * @extends Roo.BorderLayout
35906  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
35907  * center region containing two nested regions (a top one for a list view and one for item preview below),
35908  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
35909  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
35910  * expedites the setup of the overall layout and regions for this common application style.
35911  * Example:
35912  <pre><code>
35913 var reader = new Roo.ReaderLayout();
35914 var CP = Roo.ContentPanel;  // shortcut for adding
35915
35916 reader.beginUpdate();
35917 reader.add("north", new CP("north", "North"));
35918 reader.add("west", new CP("west", {title: "West"}));
35919 reader.add("east", new CP("east", {title: "East"}));
35920
35921 reader.regions.listView.add(new CP("listView", "List"));
35922 reader.regions.preview.add(new CP("preview", "Preview"));
35923 reader.endUpdate();
35924 </code></pre>
35925 * @constructor
35926 * Create a new ReaderLayout
35927 * @param {Object} config Configuration options
35928 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
35929 * document.body if omitted)
35930 */
35931 Roo.ReaderLayout = function(config, renderTo){
35932     var c = config || {size:{}};
35933     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
35934         north: c.north !== false ? Roo.apply({
35935             split:false,
35936             initialSize: 32,
35937             titlebar: false
35938         }, c.north) : false,
35939         west: c.west !== false ? Roo.apply({
35940             split:true,
35941             initialSize: 200,
35942             minSize: 175,
35943             maxSize: 400,
35944             titlebar: true,
35945             collapsible: true,
35946             animate: true,
35947             margins:{left:5,right:0,bottom:5,top:5},
35948             cmargins:{left:5,right:5,bottom:5,top:5}
35949         }, c.west) : false,
35950         east: c.east !== false ? Roo.apply({
35951             split:true,
35952             initialSize: 200,
35953             minSize: 175,
35954             maxSize: 400,
35955             titlebar: true,
35956             collapsible: true,
35957             animate: true,
35958             margins:{left:0,right:5,bottom:5,top:5},
35959             cmargins:{left:5,right:5,bottom:5,top:5}
35960         }, c.east) : false,
35961         center: Roo.apply({
35962             tabPosition: 'top',
35963             autoScroll:false,
35964             closeOnTab: true,
35965             titlebar:false,
35966             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
35967         }, c.center)
35968     });
35969
35970     this.el.addClass('x-reader');
35971
35972     this.beginUpdate();
35973
35974     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
35975         south: c.preview !== false ? Roo.apply({
35976             split:true,
35977             initialSize: 200,
35978             minSize: 100,
35979             autoScroll:true,
35980             collapsible:true,
35981             titlebar: true,
35982             cmargins:{top:5,left:0, right:0, bottom:0}
35983         }, c.preview) : false,
35984         center: Roo.apply({
35985             autoScroll:false,
35986             titlebar:false,
35987             minHeight:200
35988         }, c.listView)
35989     });
35990     this.add('center', new Roo.NestedLayoutPanel(inner,
35991             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
35992
35993     this.endUpdate();
35994
35995     this.regions.preview = inner.getRegion('south');
35996     this.regions.listView = inner.getRegion('center');
35997 };
35998
35999 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
36000  * Based on:
36001  * Ext JS Library 1.1.1
36002  * Copyright(c) 2006-2007, Ext JS, LLC.
36003  *
36004  * Originally Released Under LGPL - original licence link has changed is not relivant.
36005  *
36006  * Fork - LGPL
36007  * <script type="text/javascript">
36008  */
36009  
36010 /**
36011  * @class Roo.grid.Grid
36012  * @extends Roo.util.Observable
36013  * This class represents the primary interface of a component based grid control.
36014  * <br><br>Usage:<pre><code>
36015  var grid = new Roo.grid.Grid("my-container-id", {
36016      ds: myDataStore,
36017      cm: myColModel,
36018      selModel: mySelectionModel,
36019      autoSizeColumns: true,
36020      monitorWindowResize: false,
36021      trackMouseOver: true
36022  });
36023  // set any options
36024  grid.render();
36025  * </code></pre>
36026  * <b>Common Problems:</b><br/>
36027  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
36028  * element will correct this<br/>
36029  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
36030  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
36031  * are unpredictable.<br/>
36032  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
36033  * grid to calculate dimensions/offsets.<br/>
36034   * @constructor
36035  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36036  * The container MUST have some type of size defined for the grid to fill. The container will be
36037  * automatically set to position relative if it isn't already.
36038  * @param {Object} config A config object that sets properties on this grid.
36039  */
36040 Roo.grid.Grid = function(container, config){
36041         // initialize the container
36042         this.container = Roo.get(container);
36043         this.container.update("");
36044         this.container.setStyle("overflow", "hidden");
36045     this.container.addClass('x-grid-container');
36046
36047     this.id = this.container.id;
36048
36049     Roo.apply(this, config);
36050     // check and correct shorthanded configs
36051     if(this.ds){
36052         this.dataSource = this.ds;
36053         delete this.ds;
36054     }
36055     if(this.cm){
36056         this.colModel = this.cm;
36057         delete this.cm;
36058     }
36059     if(this.sm){
36060         this.selModel = this.sm;
36061         delete this.sm;
36062     }
36063
36064     if (this.selModel) {
36065         this.selModel = Roo.factory(this.selModel, Roo.grid);
36066         this.sm = this.selModel;
36067         this.sm.xmodule = this.xmodule || false;
36068     }
36069     if (typeof(this.colModel.config) == 'undefined') {
36070         this.colModel = new Roo.grid.ColumnModel(this.colModel);
36071         this.cm = this.colModel;
36072         this.cm.xmodule = this.xmodule || false;
36073     }
36074     if (this.dataSource) {
36075         this.dataSource= Roo.factory(this.dataSource, Roo.data);
36076         this.ds = this.dataSource;
36077         this.ds.xmodule = this.xmodule || false;
36078          
36079     }
36080     
36081     
36082     
36083     if(this.width){
36084         this.container.setWidth(this.width);
36085     }
36086
36087     if(this.height){
36088         this.container.setHeight(this.height);
36089     }
36090     /** @private */
36091         this.addEvents({
36092         // raw events
36093         /**
36094          * @event click
36095          * The raw click event for the entire grid.
36096          * @param {Roo.EventObject} e
36097          */
36098         "click" : true,
36099         /**
36100          * @event dblclick
36101          * The raw dblclick event for the entire grid.
36102          * @param {Roo.EventObject} e
36103          */
36104         "dblclick" : true,
36105         /**
36106          * @event contextmenu
36107          * The raw contextmenu event for the entire grid.
36108          * @param {Roo.EventObject} e
36109          */
36110         "contextmenu" : true,
36111         /**
36112          * @event mousedown
36113          * The raw mousedown event for the entire grid.
36114          * @param {Roo.EventObject} e
36115          */
36116         "mousedown" : true,
36117         /**
36118          * @event mouseup
36119          * The raw mouseup event for the entire grid.
36120          * @param {Roo.EventObject} e
36121          */
36122         "mouseup" : true,
36123         /**
36124          * @event mouseover
36125          * The raw mouseover event for the entire grid.
36126          * @param {Roo.EventObject} e
36127          */
36128         "mouseover" : true,
36129         /**
36130          * @event mouseout
36131          * The raw mouseout event for the entire grid.
36132          * @param {Roo.EventObject} e
36133          */
36134         "mouseout" : true,
36135         /**
36136          * @event keypress
36137          * The raw keypress event for the entire grid.
36138          * @param {Roo.EventObject} e
36139          */
36140         "keypress" : true,
36141         /**
36142          * @event keydown
36143          * The raw keydown event for the entire grid.
36144          * @param {Roo.EventObject} e
36145          */
36146         "keydown" : true,
36147
36148         // custom events
36149
36150         /**
36151          * @event cellclick
36152          * Fires when a cell is clicked
36153          * @param {Grid} this
36154          * @param {Number} rowIndex
36155          * @param {Number} columnIndex
36156          * @param {Roo.EventObject} e
36157          */
36158         "cellclick" : true,
36159         /**
36160          * @event celldblclick
36161          * Fires when a cell is double clicked
36162          * @param {Grid} this
36163          * @param {Number} rowIndex
36164          * @param {Number} columnIndex
36165          * @param {Roo.EventObject} e
36166          */
36167         "celldblclick" : true,
36168         /**
36169          * @event rowclick
36170          * Fires when a row is clicked
36171          * @param {Grid} this
36172          * @param {Number} rowIndex
36173          * @param {Roo.EventObject} e
36174          */
36175         "rowclick" : true,
36176         /**
36177          * @event rowdblclick
36178          * Fires when a row is double clicked
36179          * @param {Grid} this
36180          * @param {Number} rowIndex
36181          * @param {Roo.EventObject} e
36182          */
36183         "rowdblclick" : true,
36184         /**
36185          * @event headerclick
36186          * Fires when a header is clicked
36187          * @param {Grid} this
36188          * @param {Number} columnIndex
36189          * @param {Roo.EventObject} e
36190          */
36191         "headerclick" : true,
36192         /**
36193          * @event headerdblclick
36194          * Fires when a header cell is double clicked
36195          * @param {Grid} this
36196          * @param {Number} columnIndex
36197          * @param {Roo.EventObject} e
36198          */
36199         "headerdblclick" : true,
36200         /**
36201          * @event rowcontextmenu
36202          * Fires when a row is right clicked
36203          * @param {Grid} this
36204          * @param {Number} rowIndex
36205          * @param {Roo.EventObject} e
36206          */
36207         "rowcontextmenu" : true,
36208         /**
36209          * @event cellcontextmenu
36210          * Fires when a cell is right clicked
36211          * @param {Grid} this
36212          * @param {Number} rowIndex
36213          * @param {Number} cellIndex
36214          * @param {Roo.EventObject} e
36215          */
36216          "cellcontextmenu" : true,
36217         /**
36218          * @event headercontextmenu
36219          * Fires when a header is right clicked
36220          * @param {Grid} this
36221          * @param {Number} columnIndex
36222          * @param {Roo.EventObject} e
36223          */
36224         "headercontextmenu" : true,
36225         /**
36226          * @event bodyscroll
36227          * Fires when the body element is scrolled
36228          * @param {Number} scrollLeft
36229          * @param {Number} scrollTop
36230          */
36231         "bodyscroll" : true,
36232         /**
36233          * @event columnresize
36234          * Fires when the user resizes a column
36235          * @param {Number} columnIndex
36236          * @param {Number} newSize
36237          */
36238         "columnresize" : true,
36239         /**
36240          * @event columnmove
36241          * Fires when the user moves a column
36242          * @param {Number} oldIndex
36243          * @param {Number} newIndex
36244          */
36245         "columnmove" : true,
36246         /**
36247          * @event startdrag
36248          * Fires when row(s) start being dragged
36249          * @param {Grid} this
36250          * @param {Roo.GridDD} dd The drag drop object
36251          * @param {event} e The raw browser event
36252          */
36253         "startdrag" : true,
36254         /**
36255          * @event enddrag
36256          * Fires when a drag operation is complete
36257          * @param {Grid} this
36258          * @param {Roo.GridDD} dd The drag drop object
36259          * @param {event} e The raw browser event
36260          */
36261         "enddrag" : true,
36262         /**
36263          * @event dragdrop
36264          * Fires when dragged row(s) are dropped on a valid DD target
36265          * @param {Grid} this
36266          * @param {Roo.GridDD} dd The drag drop object
36267          * @param {String} targetId The target drag drop object
36268          * @param {event} e The raw browser event
36269          */
36270         "dragdrop" : true,
36271         /**
36272          * @event dragover
36273          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
36274          * @param {Grid} this
36275          * @param {Roo.GridDD} dd The drag drop object
36276          * @param {String} targetId The target drag drop object
36277          * @param {event} e The raw browser event
36278          */
36279         "dragover" : true,
36280         /**
36281          * @event dragenter
36282          *  Fires when the dragged row(s) first cross another DD target while being dragged
36283          * @param {Grid} this
36284          * @param {Roo.GridDD} dd The drag drop object
36285          * @param {String} targetId The target drag drop object
36286          * @param {event} e The raw browser event
36287          */
36288         "dragenter" : true,
36289         /**
36290          * @event dragout
36291          * Fires when the dragged row(s) leave another DD target while being dragged
36292          * @param {Grid} this
36293          * @param {Roo.GridDD} dd The drag drop object
36294          * @param {String} targetId The target drag drop object
36295          * @param {event} e The raw browser event
36296          */
36297         "dragout" : true,
36298         /**
36299          * @event rowclass
36300          * Fires when a row is rendered, so you can change add a style to it.
36301          * @param {GridView} gridview   The grid view
36302          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
36303          */
36304         'rowclass' : true,
36305
36306         /**
36307          * @event render
36308          * Fires when the grid is rendered
36309          * @param {Grid} grid
36310          */
36311         'render' : true
36312     });
36313
36314     Roo.grid.Grid.superclass.constructor.call(this);
36315 };
36316 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
36317     
36318     /**
36319      * @cfg {String} ddGroup - drag drop group.
36320      */
36321
36322     /**
36323      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
36324      */
36325     minColumnWidth : 25,
36326
36327     /**
36328      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
36329      * <b>on initial render.</b> It is more efficient to explicitly size the columns
36330      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
36331      */
36332     autoSizeColumns : false,
36333
36334     /**
36335      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
36336      */
36337     autoSizeHeaders : true,
36338
36339     /**
36340      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
36341      */
36342     monitorWindowResize : true,
36343
36344     /**
36345      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
36346      * rows measured to get a columns size. Default is 0 (all rows).
36347      */
36348     maxRowsToMeasure : 0,
36349
36350     /**
36351      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
36352      */
36353     trackMouseOver : true,
36354
36355     /**
36356     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
36357     */
36358     
36359     /**
36360     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
36361     */
36362     enableDragDrop : false,
36363     
36364     /**
36365     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
36366     */
36367     enableColumnMove : true,
36368     
36369     /**
36370     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
36371     */
36372     enableColumnHide : true,
36373     
36374     /**
36375     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
36376     */
36377     enableRowHeightSync : false,
36378     
36379     /**
36380     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
36381     */
36382     stripeRows : true,
36383     
36384     /**
36385     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
36386     */
36387     autoHeight : false,
36388
36389     /**
36390      * @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.
36391      */
36392     autoExpandColumn : false,
36393
36394     /**
36395     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
36396     * Default is 50.
36397     */
36398     autoExpandMin : 50,
36399
36400     /**
36401     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
36402     */
36403     autoExpandMax : 1000,
36404
36405     /**
36406     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
36407     */
36408     view : null,
36409
36410     /**
36411     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
36412     */
36413     loadMask : false,
36414     /**
36415     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
36416     */
36417     dropTarget: false,
36418     
36419    
36420     
36421     // private
36422     rendered : false,
36423
36424     /**
36425     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
36426     * of a fixed width. Default is false.
36427     */
36428     /**
36429     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
36430     */
36431     /**
36432      * Called once after all setup has been completed and the grid is ready to be rendered.
36433      * @return {Roo.grid.Grid} this
36434      */
36435     render : function()
36436     {
36437         var c = this.container;
36438         // try to detect autoHeight/width mode
36439         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
36440             this.autoHeight = true;
36441         }
36442         var view = this.getView();
36443         view.init(this);
36444
36445         c.on("click", this.onClick, this);
36446         c.on("dblclick", this.onDblClick, this);
36447         c.on("contextmenu", this.onContextMenu, this);
36448         c.on("keydown", this.onKeyDown, this);
36449         if (Roo.isTouch) {
36450             c.on("touchstart", this.onTouchStart, this);
36451         }
36452
36453         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
36454
36455         this.getSelectionModel().init(this);
36456
36457         view.render();
36458
36459         if(this.loadMask){
36460             this.loadMask = new Roo.LoadMask(this.container,
36461                     Roo.apply({store:this.dataSource}, this.loadMask));
36462         }
36463         
36464         
36465         if (this.toolbar && this.toolbar.xtype) {
36466             this.toolbar.container = this.getView().getHeaderPanel(true);
36467             this.toolbar = new Roo.Toolbar(this.toolbar);
36468         }
36469         if (this.footer && this.footer.xtype) {
36470             this.footer.dataSource = this.getDataSource();
36471             this.footer.container = this.getView().getFooterPanel(true);
36472             this.footer = Roo.factory(this.footer, Roo);
36473         }
36474         if (this.dropTarget && this.dropTarget.xtype) {
36475             delete this.dropTarget.xtype;
36476             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
36477         }
36478         
36479         
36480         this.rendered = true;
36481         this.fireEvent('render', this);
36482         return this;
36483     },
36484
36485         /**
36486          * Reconfigures the grid to use a different Store and Column Model.
36487          * The View will be bound to the new objects and refreshed.
36488          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
36489          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
36490          */
36491     reconfigure : function(dataSource, colModel){
36492         if(this.loadMask){
36493             this.loadMask.destroy();
36494             this.loadMask = new Roo.LoadMask(this.container,
36495                     Roo.apply({store:dataSource}, this.loadMask));
36496         }
36497         this.view.bind(dataSource, colModel);
36498         this.dataSource = dataSource;
36499         this.colModel = colModel;
36500         this.view.refresh(true);
36501     },
36502
36503     // private
36504     onKeyDown : function(e){
36505         this.fireEvent("keydown", e);
36506     },
36507
36508     /**
36509      * Destroy this grid.
36510      * @param {Boolean} removeEl True to remove the element
36511      */
36512     destroy : function(removeEl, keepListeners){
36513         if(this.loadMask){
36514             this.loadMask.destroy();
36515         }
36516         var c = this.container;
36517         c.removeAllListeners();
36518         this.view.destroy();
36519         this.colModel.purgeListeners();
36520         if(!keepListeners){
36521             this.purgeListeners();
36522         }
36523         c.update("");
36524         if(removeEl === true){
36525             c.remove();
36526         }
36527     },
36528
36529     // private
36530     processEvent : function(name, e){
36531         // does this fire select???
36532         //Roo.log('grid:processEvent '  + name);
36533         
36534         if (name != 'touchstart' ) {
36535             this.fireEvent(name, e);    
36536         }
36537         
36538         var t = e.getTarget();
36539         var v = this.view;
36540         var header = v.findHeaderIndex(t);
36541         if(header !== false){
36542             var ename = name == 'touchstart' ? 'click' : name;
36543              
36544             this.fireEvent("header" + ename, this, header, e);
36545         }else{
36546             var row = v.findRowIndex(t);
36547             var cell = v.findCellIndex(t);
36548             if (name == 'touchstart') {
36549                 // first touch is always a click.
36550                 // hopefull this happens after selection is updated.?
36551                 name = false;
36552                 
36553                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
36554                     var cs = this.selModel.getSelectedCell();
36555                     if (row == cs[0] && cell == cs[1]){
36556                         name = 'dblclick';
36557                     }
36558                 }
36559                 if (typeof(this.selModel.getSelections) != 'undefined') {
36560                     var cs = this.selModel.getSelections();
36561                     var ds = this.dataSource;
36562                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
36563                         name = 'dblclick';
36564                     }
36565                 }
36566                 if (!name) {
36567                     return;
36568                 }
36569             }
36570             
36571             
36572             if(row !== false){
36573                 this.fireEvent("row" + name, this, row, e);
36574                 if(cell !== false){
36575                     this.fireEvent("cell" + name, this, row, cell, e);
36576                 }
36577             }
36578         }
36579     },
36580
36581     // private
36582     onClick : function(e){
36583         this.processEvent("click", e);
36584     },
36585    // private
36586     onTouchStart : function(e){
36587         this.processEvent("touchstart", e);
36588     },
36589
36590     // private
36591     onContextMenu : function(e, t){
36592         this.processEvent("contextmenu", e);
36593     },
36594
36595     // private
36596     onDblClick : function(e){
36597         this.processEvent("dblclick", e);
36598     },
36599
36600     // private
36601     walkCells : function(row, col, step, fn, scope){
36602         var cm = this.colModel, clen = cm.getColumnCount();
36603         var ds = this.dataSource, rlen = ds.getCount(), first = true;
36604         if(step < 0){
36605             if(col < 0){
36606                 row--;
36607                 first = false;
36608             }
36609             while(row >= 0){
36610                 if(!first){
36611                     col = clen-1;
36612                 }
36613                 first = false;
36614                 while(col >= 0){
36615                     if(fn.call(scope || this, row, col, cm) === true){
36616                         return [row, col];
36617                     }
36618                     col--;
36619                 }
36620                 row--;
36621             }
36622         } else {
36623             if(col >= clen){
36624                 row++;
36625                 first = false;
36626             }
36627             while(row < rlen){
36628                 if(!first){
36629                     col = 0;
36630                 }
36631                 first = false;
36632                 while(col < clen){
36633                     if(fn.call(scope || this, row, col, cm) === true){
36634                         return [row, col];
36635                     }
36636                     col++;
36637                 }
36638                 row++;
36639             }
36640         }
36641         return null;
36642     },
36643
36644     // private
36645     getSelections : function(){
36646         return this.selModel.getSelections();
36647     },
36648
36649     /**
36650      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
36651      * but if manual update is required this method will initiate it.
36652      */
36653     autoSize : function(){
36654         if(this.rendered){
36655             this.view.layout();
36656             if(this.view.adjustForScroll){
36657                 this.view.adjustForScroll();
36658             }
36659         }
36660     },
36661
36662     /**
36663      * Returns the grid's underlying element.
36664      * @return {Element} The element
36665      */
36666     getGridEl : function(){
36667         return this.container;
36668     },
36669
36670     // private for compatibility, overridden by editor grid
36671     stopEditing : function(){},
36672
36673     /**
36674      * Returns the grid's SelectionModel.
36675      * @return {SelectionModel}
36676      */
36677     getSelectionModel : function(){
36678         if(!this.selModel){
36679             this.selModel = new Roo.grid.RowSelectionModel();
36680         }
36681         return this.selModel;
36682     },
36683
36684     /**
36685      * Returns the grid's DataSource.
36686      * @return {DataSource}
36687      */
36688     getDataSource : function(){
36689         return this.dataSource;
36690     },
36691
36692     /**
36693      * Returns the grid's ColumnModel.
36694      * @return {ColumnModel}
36695      */
36696     getColumnModel : function(){
36697         return this.colModel;
36698     },
36699
36700     /**
36701      * Returns the grid's GridView object.
36702      * @return {GridView}
36703      */
36704     getView : function(){
36705         if(!this.view){
36706             this.view = new Roo.grid.GridView(this.viewConfig);
36707         }
36708         return this.view;
36709     },
36710     /**
36711      * Called to get grid's drag proxy text, by default returns this.ddText.
36712      * @return {String}
36713      */
36714     getDragDropText : function(){
36715         var count = this.selModel.getCount();
36716         return String.format(this.ddText, count, count == 1 ? '' : 's');
36717     }
36718 });
36719 /**
36720  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
36721  * %0 is replaced with the number of selected rows.
36722  * @type String
36723  */
36724 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
36725  * Based on:
36726  * Ext JS Library 1.1.1
36727  * Copyright(c) 2006-2007, Ext JS, LLC.
36728  *
36729  * Originally Released Under LGPL - original licence link has changed is not relivant.
36730  *
36731  * Fork - LGPL
36732  * <script type="text/javascript">
36733  */
36734  
36735 Roo.grid.AbstractGridView = function(){
36736         this.grid = null;
36737         
36738         this.events = {
36739             "beforerowremoved" : true,
36740             "beforerowsinserted" : true,
36741             "beforerefresh" : true,
36742             "rowremoved" : true,
36743             "rowsinserted" : true,
36744             "rowupdated" : true,
36745             "refresh" : true
36746         };
36747     Roo.grid.AbstractGridView.superclass.constructor.call(this);
36748 };
36749
36750 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
36751     rowClass : "x-grid-row",
36752     cellClass : "x-grid-cell",
36753     tdClass : "x-grid-td",
36754     hdClass : "x-grid-hd",
36755     splitClass : "x-grid-hd-split",
36756     
36757     init: function(grid){
36758         this.grid = grid;
36759                 var cid = this.grid.getGridEl().id;
36760         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
36761         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
36762         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
36763         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
36764         },
36765         
36766     getColumnRenderers : function(){
36767         var renderers = [];
36768         var cm = this.grid.colModel;
36769         var colCount = cm.getColumnCount();
36770         for(var i = 0; i < colCount; i++){
36771             renderers[i] = cm.getRenderer(i);
36772         }
36773         return renderers;
36774     },
36775     
36776     getColumnIds : function(){
36777         var ids = [];
36778         var cm = this.grid.colModel;
36779         var colCount = cm.getColumnCount();
36780         for(var i = 0; i < colCount; i++){
36781             ids[i] = cm.getColumnId(i);
36782         }
36783         return ids;
36784     },
36785     
36786     getDataIndexes : function(){
36787         if(!this.indexMap){
36788             this.indexMap = this.buildIndexMap();
36789         }
36790         return this.indexMap.colToData;
36791     },
36792     
36793     getColumnIndexByDataIndex : function(dataIndex){
36794         if(!this.indexMap){
36795             this.indexMap = this.buildIndexMap();
36796         }
36797         return this.indexMap.dataToCol[dataIndex];
36798     },
36799     
36800     /**
36801      * Set a css style for a column dynamically. 
36802      * @param {Number} colIndex The index of the column
36803      * @param {String} name The css property name
36804      * @param {String} value The css value
36805      */
36806     setCSSStyle : function(colIndex, name, value){
36807         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
36808         Roo.util.CSS.updateRule(selector, name, value);
36809     },
36810     
36811     generateRules : function(cm){
36812         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
36813         Roo.util.CSS.removeStyleSheet(rulesId);
36814         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36815             var cid = cm.getColumnId(i);
36816             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
36817                          this.tdSelector, cid, " {\n}\n",
36818                          this.hdSelector, cid, " {\n}\n",
36819                          this.splitSelector, cid, " {\n}\n");
36820         }
36821         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
36822     }
36823 });/*
36824  * Based on:
36825  * Ext JS Library 1.1.1
36826  * Copyright(c) 2006-2007, Ext JS, LLC.
36827  *
36828  * Originally Released Under LGPL - original licence link has changed is not relivant.
36829  *
36830  * Fork - LGPL
36831  * <script type="text/javascript">
36832  */
36833
36834 // private
36835 // This is a support class used internally by the Grid components
36836 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
36837     this.grid = grid;
36838     this.view = grid.getView();
36839     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36840     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
36841     if(hd2){
36842         this.setHandleElId(Roo.id(hd));
36843         this.setOuterHandleElId(Roo.id(hd2));
36844     }
36845     this.scroll = false;
36846 };
36847 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
36848     maxDragWidth: 120,
36849     getDragData : function(e){
36850         var t = Roo.lib.Event.getTarget(e);
36851         var h = this.view.findHeaderCell(t);
36852         if(h){
36853             return {ddel: h.firstChild, header:h};
36854         }
36855         return false;
36856     },
36857
36858     onInitDrag : function(e){
36859         this.view.headersDisabled = true;
36860         var clone = this.dragData.ddel.cloneNode(true);
36861         clone.id = Roo.id();
36862         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
36863         this.proxy.update(clone);
36864         return true;
36865     },
36866
36867     afterValidDrop : function(){
36868         var v = this.view;
36869         setTimeout(function(){
36870             v.headersDisabled = false;
36871         }, 50);
36872     },
36873
36874     afterInvalidDrop : function(){
36875         var v = this.view;
36876         setTimeout(function(){
36877             v.headersDisabled = false;
36878         }, 50);
36879     }
36880 });
36881 /*
36882  * Based on:
36883  * Ext JS Library 1.1.1
36884  * Copyright(c) 2006-2007, Ext JS, LLC.
36885  *
36886  * Originally Released Under LGPL - original licence link has changed is not relivant.
36887  *
36888  * Fork - LGPL
36889  * <script type="text/javascript">
36890  */
36891 // private
36892 // This is a support class used internally by the Grid components
36893 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
36894     this.grid = grid;
36895     this.view = grid.getView();
36896     // split the proxies so they don't interfere with mouse events
36897     this.proxyTop = Roo.DomHelper.append(document.body, {
36898         cls:"col-move-top", html:"&#160;"
36899     }, true);
36900     this.proxyBottom = Roo.DomHelper.append(document.body, {
36901         cls:"col-move-bottom", html:"&#160;"
36902     }, true);
36903     this.proxyTop.hide = this.proxyBottom.hide = function(){
36904         this.setLeftTop(-100,-100);
36905         this.setStyle("visibility", "hidden");
36906     };
36907     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36908     // temporarily disabled
36909     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
36910     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
36911 };
36912 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
36913     proxyOffsets : [-4, -9],
36914     fly: Roo.Element.fly,
36915
36916     getTargetFromEvent : function(e){
36917         var t = Roo.lib.Event.getTarget(e);
36918         var cindex = this.view.findCellIndex(t);
36919         if(cindex !== false){
36920             return this.view.getHeaderCell(cindex);
36921         }
36922         return null;
36923     },
36924
36925     nextVisible : function(h){
36926         var v = this.view, cm = this.grid.colModel;
36927         h = h.nextSibling;
36928         while(h){
36929             if(!cm.isHidden(v.getCellIndex(h))){
36930                 return h;
36931             }
36932             h = h.nextSibling;
36933         }
36934         return null;
36935     },
36936
36937     prevVisible : function(h){
36938         var v = this.view, cm = this.grid.colModel;
36939         h = h.prevSibling;
36940         while(h){
36941             if(!cm.isHidden(v.getCellIndex(h))){
36942                 return h;
36943             }
36944             h = h.prevSibling;
36945         }
36946         return null;
36947     },
36948
36949     positionIndicator : function(h, n, e){
36950         var x = Roo.lib.Event.getPageX(e);
36951         var r = Roo.lib.Dom.getRegion(n.firstChild);
36952         var px, pt, py = r.top + this.proxyOffsets[1];
36953         if((r.right - x) <= (r.right-r.left)/2){
36954             px = r.right+this.view.borderWidth;
36955             pt = "after";
36956         }else{
36957             px = r.left;
36958             pt = "before";
36959         }
36960         var oldIndex = this.view.getCellIndex(h);
36961         var newIndex = this.view.getCellIndex(n);
36962
36963         if(this.grid.colModel.isFixed(newIndex)){
36964             return false;
36965         }
36966
36967         var locked = this.grid.colModel.isLocked(newIndex);
36968
36969         if(pt == "after"){
36970             newIndex++;
36971         }
36972         if(oldIndex < newIndex){
36973             newIndex--;
36974         }
36975         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
36976             return false;
36977         }
36978         px +=  this.proxyOffsets[0];
36979         this.proxyTop.setLeftTop(px, py);
36980         this.proxyTop.show();
36981         if(!this.bottomOffset){
36982             this.bottomOffset = this.view.mainHd.getHeight();
36983         }
36984         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
36985         this.proxyBottom.show();
36986         return pt;
36987     },
36988
36989     onNodeEnter : function(n, dd, e, data){
36990         if(data.header != n){
36991             this.positionIndicator(data.header, n, e);
36992         }
36993     },
36994
36995     onNodeOver : function(n, dd, e, data){
36996         var result = false;
36997         if(data.header != n){
36998             result = this.positionIndicator(data.header, n, e);
36999         }
37000         if(!result){
37001             this.proxyTop.hide();
37002             this.proxyBottom.hide();
37003         }
37004         return result ? this.dropAllowed : this.dropNotAllowed;
37005     },
37006
37007     onNodeOut : function(n, dd, e, data){
37008         this.proxyTop.hide();
37009         this.proxyBottom.hide();
37010     },
37011
37012     onNodeDrop : function(n, dd, e, data){
37013         var h = data.header;
37014         if(h != n){
37015             var cm = this.grid.colModel;
37016             var x = Roo.lib.Event.getPageX(e);
37017             var r = Roo.lib.Dom.getRegion(n.firstChild);
37018             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
37019             var oldIndex = this.view.getCellIndex(h);
37020             var newIndex = this.view.getCellIndex(n);
37021             var locked = cm.isLocked(newIndex);
37022             if(pt == "after"){
37023                 newIndex++;
37024             }
37025             if(oldIndex < newIndex){
37026                 newIndex--;
37027             }
37028             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
37029                 return false;
37030             }
37031             cm.setLocked(oldIndex, locked, true);
37032             cm.moveColumn(oldIndex, newIndex);
37033             this.grid.fireEvent("columnmove", oldIndex, newIndex);
37034             return true;
37035         }
37036         return false;
37037     }
37038 });
37039 /*
37040  * Based on:
37041  * Ext JS Library 1.1.1
37042  * Copyright(c) 2006-2007, Ext JS, LLC.
37043  *
37044  * Originally Released Under LGPL - original licence link has changed is not relivant.
37045  *
37046  * Fork - LGPL
37047  * <script type="text/javascript">
37048  */
37049   
37050 /**
37051  * @class Roo.grid.GridView
37052  * @extends Roo.util.Observable
37053  *
37054  * @constructor
37055  * @param {Object} config
37056  */
37057 Roo.grid.GridView = function(config){
37058     Roo.grid.GridView.superclass.constructor.call(this);
37059     this.el = null;
37060
37061     Roo.apply(this, config);
37062 };
37063
37064 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
37065
37066     unselectable :  'unselectable="on"',
37067     unselectableCls :  'x-unselectable',
37068     
37069     
37070     rowClass : "x-grid-row",
37071
37072     cellClass : "x-grid-col",
37073
37074     tdClass : "x-grid-td",
37075
37076     hdClass : "x-grid-hd",
37077
37078     splitClass : "x-grid-split",
37079
37080     sortClasses : ["sort-asc", "sort-desc"],
37081
37082     enableMoveAnim : false,
37083
37084     hlColor: "C3DAF9",
37085
37086     dh : Roo.DomHelper,
37087
37088     fly : Roo.Element.fly,
37089
37090     css : Roo.util.CSS,
37091
37092     borderWidth: 1,
37093
37094     splitOffset: 3,
37095
37096     scrollIncrement : 22,
37097
37098     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
37099
37100     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
37101
37102     bind : function(ds, cm){
37103         if(this.ds){
37104             this.ds.un("load", this.onLoad, this);
37105             this.ds.un("datachanged", this.onDataChange, this);
37106             this.ds.un("add", this.onAdd, this);
37107             this.ds.un("remove", this.onRemove, this);
37108             this.ds.un("update", this.onUpdate, this);
37109             this.ds.un("clear", this.onClear, this);
37110         }
37111         if(ds){
37112             ds.on("load", this.onLoad, this);
37113             ds.on("datachanged", this.onDataChange, this);
37114             ds.on("add", this.onAdd, this);
37115             ds.on("remove", this.onRemove, this);
37116             ds.on("update", this.onUpdate, this);
37117             ds.on("clear", this.onClear, this);
37118         }
37119         this.ds = ds;
37120
37121         if(this.cm){
37122             this.cm.un("widthchange", this.onColWidthChange, this);
37123             this.cm.un("headerchange", this.onHeaderChange, this);
37124             this.cm.un("hiddenchange", this.onHiddenChange, this);
37125             this.cm.un("columnmoved", this.onColumnMove, this);
37126             this.cm.un("columnlockchange", this.onColumnLock, this);
37127         }
37128         if(cm){
37129             this.generateRules(cm);
37130             cm.on("widthchange", this.onColWidthChange, this);
37131             cm.on("headerchange", this.onHeaderChange, this);
37132             cm.on("hiddenchange", this.onHiddenChange, this);
37133             cm.on("columnmoved", this.onColumnMove, this);
37134             cm.on("columnlockchange", this.onColumnLock, this);
37135         }
37136         this.cm = cm;
37137     },
37138
37139     init: function(grid){
37140         Roo.grid.GridView.superclass.init.call(this, grid);
37141
37142         this.bind(grid.dataSource, grid.colModel);
37143
37144         grid.on("headerclick", this.handleHeaderClick, this);
37145
37146         if(grid.trackMouseOver){
37147             grid.on("mouseover", this.onRowOver, this);
37148             grid.on("mouseout", this.onRowOut, this);
37149         }
37150         grid.cancelTextSelection = function(){};
37151         this.gridId = grid.id;
37152
37153         var tpls = this.templates || {};
37154
37155         if(!tpls.master){
37156             tpls.master = new Roo.Template(
37157                '<div class="x-grid" hidefocus="true">',
37158                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
37159                   '<div class="x-grid-topbar"></div>',
37160                   '<div class="x-grid-scroller"><div></div></div>',
37161                   '<div class="x-grid-locked">',
37162                       '<div class="x-grid-header">{lockedHeader}</div>',
37163                       '<div class="x-grid-body">{lockedBody}</div>',
37164                   "</div>",
37165                   '<div class="x-grid-viewport">',
37166                       '<div class="x-grid-header">{header}</div>',
37167                       '<div class="x-grid-body">{body}</div>',
37168                   "</div>",
37169                   '<div class="x-grid-bottombar"></div>',
37170                  
37171                   '<div class="x-grid-resize-proxy">&#160;</div>',
37172                "</div>"
37173             );
37174             tpls.master.disableformats = true;
37175         }
37176
37177         if(!tpls.header){
37178             tpls.header = new Roo.Template(
37179                '<table border="0" cellspacing="0" cellpadding="0">',
37180                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
37181                "</table>{splits}"
37182             );
37183             tpls.header.disableformats = true;
37184         }
37185         tpls.header.compile();
37186
37187         if(!tpls.hcell){
37188             tpls.hcell = new Roo.Template(
37189                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
37190                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
37191                 "</div></td>"
37192              );
37193              tpls.hcell.disableFormats = true;
37194         }
37195         tpls.hcell.compile();
37196
37197         if(!tpls.hsplit){
37198             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
37199                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
37200             tpls.hsplit.disableFormats = true;
37201         }
37202         tpls.hsplit.compile();
37203
37204         if(!tpls.body){
37205             tpls.body = new Roo.Template(
37206                '<table border="0" cellspacing="0" cellpadding="0">',
37207                "<tbody>{rows}</tbody>",
37208                "</table>"
37209             );
37210             tpls.body.disableFormats = true;
37211         }
37212         tpls.body.compile();
37213
37214         if(!tpls.row){
37215             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
37216             tpls.row.disableFormats = true;
37217         }
37218         tpls.row.compile();
37219
37220         if(!tpls.cell){
37221             tpls.cell = new Roo.Template(
37222                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
37223                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
37224                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
37225                 "</td>"
37226             );
37227             tpls.cell.disableFormats = true;
37228         }
37229         tpls.cell.compile();
37230
37231         this.templates = tpls;
37232     },
37233
37234     // remap these for backwards compat
37235     onColWidthChange : function(){
37236         this.updateColumns.apply(this, arguments);
37237     },
37238     onHeaderChange : function(){
37239         this.updateHeaders.apply(this, arguments);
37240     }, 
37241     onHiddenChange : function(){
37242         this.handleHiddenChange.apply(this, arguments);
37243     },
37244     onColumnMove : function(){
37245         this.handleColumnMove.apply(this, arguments);
37246     },
37247     onColumnLock : function(){
37248         this.handleLockChange.apply(this, arguments);
37249     },
37250
37251     onDataChange : function(){
37252         this.refresh();
37253         this.updateHeaderSortState();
37254     },
37255
37256     onClear : function(){
37257         this.refresh();
37258     },
37259
37260     onUpdate : function(ds, record){
37261         this.refreshRow(record);
37262     },
37263
37264     refreshRow : function(record){
37265         var ds = this.ds, index;
37266         if(typeof record == 'number'){
37267             index = record;
37268             record = ds.getAt(index);
37269         }else{
37270             index = ds.indexOf(record);
37271         }
37272         this.insertRows(ds, index, index, true);
37273         this.onRemove(ds, record, index+1, true);
37274         this.syncRowHeights(index, index);
37275         this.layout();
37276         this.fireEvent("rowupdated", this, index, record);
37277     },
37278
37279     onAdd : function(ds, records, index){
37280         this.insertRows(ds, index, index + (records.length-1));
37281     },
37282
37283     onRemove : function(ds, record, index, isUpdate){
37284         if(isUpdate !== true){
37285             this.fireEvent("beforerowremoved", this, index, record);
37286         }
37287         var bt = this.getBodyTable(), lt = this.getLockedTable();
37288         if(bt.rows[index]){
37289             bt.firstChild.removeChild(bt.rows[index]);
37290         }
37291         if(lt.rows[index]){
37292             lt.firstChild.removeChild(lt.rows[index]);
37293         }
37294         if(isUpdate !== true){
37295             this.stripeRows(index);
37296             this.syncRowHeights(index, index);
37297             this.layout();
37298             this.fireEvent("rowremoved", this, index, record);
37299         }
37300     },
37301
37302     onLoad : function(){
37303         this.scrollToTop();
37304     },
37305
37306     /**
37307      * Scrolls the grid to the top
37308      */
37309     scrollToTop : function(){
37310         if(this.scroller){
37311             this.scroller.dom.scrollTop = 0;
37312             this.syncScroll();
37313         }
37314     },
37315
37316     /**
37317      * Gets a panel in the header of the grid that can be used for toolbars etc.
37318      * After modifying the contents of this panel a call to grid.autoSize() may be
37319      * required to register any changes in size.
37320      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
37321      * @return Roo.Element
37322      */
37323     getHeaderPanel : function(doShow){
37324         if(doShow){
37325             this.headerPanel.show();
37326         }
37327         return this.headerPanel;
37328     },
37329
37330     /**
37331      * Gets a panel in the footer of the grid that can be used for toolbars etc.
37332      * After modifying the contents of this panel a call to grid.autoSize() may be
37333      * required to register any changes in size.
37334      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
37335      * @return Roo.Element
37336      */
37337     getFooterPanel : function(doShow){
37338         if(doShow){
37339             this.footerPanel.show();
37340         }
37341         return this.footerPanel;
37342     },
37343
37344     initElements : function(){
37345         var E = Roo.Element;
37346         var el = this.grid.getGridEl().dom.firstChild;
37347         var cs = el.childNodes;
37348
37349         this.el = new E(el);
37350         
37351          this.focusEl = new E(el.firstChild);
37352         this.focusEl.swallowEvent("click", true);
37353         
37354         this.headerPanel = new E(cs[1]);
37355         this.headerPanel.enableDisplayMode("block");
37356
37357         this.scroller = new E(cs[2]);
37358         this.scrollSizer = new E(this.scroller.dom.firstChild);
37359
37360         this.lockedWrap = new E(cs[3]);
37361         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
37362         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
37363
37364         this.mainWrap = new E(cs[4]);
37365         this.mainHd = new E(this.mainWrap.dom.firstChild);
37366         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
37367
37368         this.footerPanel = new E(cs[5]);
37369         this.footerPanel.enableDisplayMode("block");
37370
37371         this.resizeProxy = new E(cs[6]);
37372
37373         this.headerSelector = String.format(
37374            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
37375            this.lockedHd.id, this.mainHd.id
37376         );
37377
37378         this.splitterSelector = String.format(
37379            '#{0} div.x-grid-split, #{1} div.x-grid-split',
37380            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
37381         );
37382     },
37383     idToCssName : function(s)
37384     {
37385         return s.replace(/[^a-z0-9]+/ig, '-');
37386     },
37387
37388     getHeaderCell : function(index){
37389         return Roo.DomQuery.select(this.headerSelector)[index];
37390     },
37391
37392     getHeaderCellMeasure : function(index){
37393         return this.getHeaderCell(index).firstChild;
37394     },
37395
37396     getHeaderCellText : function(index){
37397         return this.getHeaderCell(index).firstChild.firstChild;
37398     },
37399
37400     getLockedTable : function(){
37401         return this.lockedBody.dom.firstChild;
37402     },
37403
37404     getBodyTable : function(){
37405         return this.mainBody.dom.firstChild;
37406     },
37407
37408     getLockedRow : function(index){
37409         return this.getLockedTable().rows[index];
37410     },
37411
37412     getRow : function(index){
37413         return this.getBodyTable().rows[index];
37414     },
37415
37416     getRowComposite : function(index){
37417         if(!this.rowEl){
37418             this.rowEl = new Roo.CompositeElementLite();
37419         }
37420         var els = [], lrow, mrow;
37421         if(lrow = this.getLockedRow(index)){
37422             els.push(lrow);
37423         }
37424         if(mrow = this.getRow(index)){
37425             els.push(mrow);
37426         }
37427         this.rowEl.elements = els;
37428         return this.rowEl;
37429     },
37430     /**
37431      * Gets the 'td' of the cell
37432      * 
37433      * @param {Integer} rowIndex row to select
37434      * @param {Integer} colIndex column to select
37435      * 
37436      * @return {Object} 
37437      */
37438     getCell : function(rowIndex, colIndex){
37439         var locked = this.cm.getLockedCount();
37440         var source;
37441         if(colIndex < locked){
37442             source = this.lockedBody.dom.firstChild;
37443         }else{
37444             source = this.mainBody.dom.firstChild;
37445             colIndex -= locked;
37446         }
37447         return source.rows[rowIndex].childNodes[colIndex];
37448     },
37449
37450     getCellText : function(rowIndex, colIndex){
37451         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
37452     },
37453
37454     getCellBox : function(cell){
37455         var b = this.fly(cell).getBox();
37456         if(Roo.isOpera){ // opera fails to report the Y
37457             b.y = cell.offsetTop + this.mainBody.getY();
37458         }
37459         return b;
37460     },
37461
37462     getCellIndex : function(cell){
37463         var id = String(cell.className).match(this.cellRE);
37464         if(id){
37465             return parseInt(id[1], 10);
37466         }
37467         return 0;
37468     },
37469
37470     findHeaderIndex : function(n){
37471         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
37472         return r ? this.getCellIndex(r) : false;
37473     },
37474
37475     findHeaderCell : function(n){
37476         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
37477         return r ? r : false;
37478     },
37479
37480     findRowIndex : function(n){
37481         if(!n){
37482             return false;
37483         }
37484         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
37485         return r ? r.rowIndex : false;
37486     },
37487
37488     findCellIndex : function(node){
37489         var stop = this.el.dom;
37490         while(node && node != stop){
37491             if(this.findRE.test(node.className)){
37492                 return this.getCellIndex(node);
37493             }
37494             node = node.parentNode;
37495         }
37496         return false;
37497     },
37498
37499     getColumnId : function(index){
37500         return this.cm.getColumnId(index);
37501     },
37502
37503     getSplitters : function()
37504     {
37505         if(this.splitterSelector){
37506            return Roo.DomQuery.select(this.splitterSelector);
37507         }else{
37508             return null;
37509       }
37510     },
37511
37512     getSplitter : function(index){
37513         return this.getSplitters()[index];
37514     },
37515
37516     onRowOver : function(e, t){
37517         var row;
37518         if((row = this.findRowIndex(t)) !== false){
37519             this.getRowComposite(row).addClass("x-grid-row-over");
37520         }
37521     },
37522
37523     onRowOut : function(e, t){
37524         var row;
37525         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
37526             this.getRowComposite(row).removeClass("x-grid-row-over");
37527         }
37528     },
37529
37530     renderHeaders : function(){
37531         var cm = this.cm;
37532         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
37533         var cb = [], lb = [], sb = [], lsb = [], p = {};
37534         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37535             p.cellId = "x-grid-hd-0-" + i;
37536             p.splitId = "x-grid-csplit-0-" + i;
37537             p.id = cm.getColumnId(i);
37538             p.value = cm.getColumnHeader(i) || "";
37539             p.title = cm.getColumnTooltip(i) || p.value.match(/\</)  ? '' :  p.value  || "";
37540             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
37541             if(!cm.isLocked(i)){
37542                 cb[cb.length] = ct.apply(p);
37543                 sb[sb.length] = st.apply(p);
37544             }else{
37545                 lb[lb.length] = ct.apply(p);
37546                 lsb[lsb.length] = st.apply(p);
37547             }
37548         }
37549         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
37550                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
37551     },
37552
37553     updateHeaders : function(){
37554         var html = this.renderHeaders();
37555         this.lockedHd.update(html[0]);
37556         this.mainHd.update(html[1]);
37557     },
37558
37559     /**
37560      * Focuses the specified row.
37561      * @param {Number} row The row index
37562      */
37563     focusRow : function(row)
37564     {
37565         //Roo.log('GridView.focusRow');
37566         var x = this.scroller.dom.scrollLeft;
37567         this.focusCell(row, 0, false);
37568         this.scroller.dom.scrollLeft = x;
37569     },
37570
37571     /**
37572      * Focuses the specified cell.
37573      * @param {Number} row The row index
37574      * @param {Number} col The column index
37575      * @param {Boolean} hscroll false to disable horizontal scrolling
37576      */
37577     focusCell : function(row, col, hscroll)
37578     {
37579         //Roo.log('GridView.focusCell');
37580         var el = this.ensureVisible(row, col, hscroll);
37581         this.focusEl.alignTo(el, "tl-tl");
37582         if(Roo.isGecko){
37583             this.focusEl.focus();
37584         }else{
37585             this.focusEl.focus.defer(1, this.focusEl);
37586         }
37587     },
37588
37589     /**
37590      * Scrolls the specified cell into view
37591      * @param {Number} row The row index
37592      * @param {Number} col The column index
37593      * @param {Boolean} hscroll false to disable horizontal scrolling
37594      */
37595     ensureVisible : function(row, col, hscroll)
37596     {
37597         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
37598         //return null; //disable for testing.
37599         if(typeof row != "number"){
37600             row = row.rowIndex;
37601         }
37602         if(row < 0 && row >= this.ds.getCount()){
37603             return  null;
37604         }
37605         col = (col !== undefined ? col : 0);
37606         var cm = this.grid.colModel;
37607         while(cm.isHidden(col)){
37608             col++;
37609         }
37610
37611         var el = this.getCell(row, col);
37612         if(!el){
37613             return null;
37614         }
37615         var c = this.scroller.dom;
37616
37617         var ctop = parseInt(el.offsetTop, 10);
37618         var cleft = parseInt(el.offsetLeft, 10);
37619         var cbot = ctop + el.offsetHeight;
37620         var cright = cleft + el.offsetWidth;
37621         
37622         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
37623         var stop = parseInt(c.scrollTop, 10);
37624         var sleft = parseInt(c.scrollLeft, 10);
37625         var sbot = stop + ch;
37626         var sright = sleft + c.clientWidth;
37627         /*
37628         Roo.log('GridView.ensureVisible:' +
37629                 ' ctop:' + ctop +
37630                 ' c.clientHeight:' + c.clientHeight +
37631                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
37632                 ' stop:' + stop +
37633                 ' cbot:' + cbot +
37634                 ' sbot:' + sbot +
37635                 ' ch:' + ch  
37636                 );
37637         */
37638         if(ctop < stop){
37639              c.scrollTop = ctop;
37640             //Roo.log("set scrolltop to ctop DISABLE?");
37641         }else if(cbot > sbot){
37642             //Roo.log("set scrolltop to cbot-ch");
37643             c.scrollTop = cbot-ch;
37644         }
37645         
37646         if(hscroll !== false){
37647             if(cleft < sleft){
37648                 c.scrollLeft = cleft;
37649             }else if(cright > sright){
37650                 c.scrollLeft = cright-c.clientWidth;
37651             }
37652         }
37653          
37654         return el;
37655     },
37656
37657     updateColumns : function(){
37658         this.grid.stopEditing();
37659         var cm = this.grid.colModel, colIds = this.getColumnIds();
37660         //var totalWidth = cm.getTotalWidth();
37661         var pos = 0;
37662         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37663             //if(cm.isHidden(i)) continue;
37664             var w = cm.getColumnWidth(i);
37665             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37666             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37667         }
37668         this.updateSplitters();
37669     },
37670
37671     generateRules : function(cm){
37672         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
37673         Roo.util.CSS.removeStyleSheet(rulesId);
37674         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37675             var cid = cm.getColumnId(i);
37676             var align = '';
37677             if(cm.config[i].align){
37678                 align = 'text-align:'+cm.config[i].align+';';
37679             }
37680             var hidden = '';
37681             if(cm.isHidden(i)){
37682                 hidden = 'display:none;';
37683             }
37684             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
37685             ruleBuf.push(
37686                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
37687                     this.hdSelector, cid, " {\n", align, width, "}\n",
37688                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
37689                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
37690         }
37691         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37692     },
37693
37694     updateSplitters : function(){
37695         var cm = this.cm, s = this.getSplitters();
37696         if(s){ // splitters not created yet
37697             var pos = 0, locked = true;
37698             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37699                 if(cm.isHidden(i)) {
37700                     continue;
37701                 }
37702                 var w = cm.getColumnWidth(i); // make sure it's a number
37703                 if(!cm.isLocked(i) && locked){
37704                     pos = 0;
37705                     locked = false;
37706                 }
37707                 pos += w;
37708                 s[i].style.left = (pos-this.splitOffset) + "px";
37709             }
37710         }
37711     },
37712
37713     handleHiddenChange : function(colModel, colIndex, hidden){
37714         if(hidden){
37715             this.hideColumn(colIndex);
37716         }else{
37717             this.unhideColumn(colIndex);
37718         }
37719     },
37720
37721     hideColumn : function(colIndex){
37722         var cid = this.getColumnId(colIndex);
37723         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
37724         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
37725         if(Roo.isSafari){
37726             this.updateHeaders();
37727         }
37728         this.updateSplitters();
37729         this.layout();
37730     },
37731
37732     unhideColumn : function(colIndex){
37733         var cid = this.getColumnId(colIndex);
37734         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
37735         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
37736
37737         if(Roo.isSafari){
37738             this.updateHeaders();
37739         }
37740         this.updateSplitters();
37741         this.layout();
37742     },
37743
37744     insertRows : function(dm, firstRow, lastRow, isUpdate){
37745         if(firstRow == 0 && lastRow == dm.getCount()-1){
37746             this.refresh();
37747         }else{
37748             if(!isUpdate){
37749                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
37750             }
37751             var s = this.getScrollState();
37752             var markup = this.renderRows(firstRow, lastRow);
37753             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
37754             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
37755             this.restoreScroll(s);
37756             if(!isUpdate){
37757                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
37758                 this.syncRowHeights(firstRow, lastRow);
37759                 this.stripeRows(firstRow);
37760                 this.layout();
37761             }
37762         }
37763     },
37764
37765     bufferRows : function(markup, target, index){
37766         var before = null, trows = target.rows, tbody = target.tBodies[0];
37767         if(index < trows.length){
37768             before = trows[index];
37769         }
37770         var b = document.createElement("div");
37771         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
37772         var rows = b.firstChild.rows;
37773         for(var i = 0, len = rows.length; i < len; i++){
37774             if(before){
37775                 tbody.insertBefore(rows[0], before);
37776             }else{
37777                 tbody.appendChild(rows[0]);
37778             }
37779         }
37780         b.innerHTML = "";
37781         b = null;
37782     },
37783
37784     deleteRows : function(dm, firstRow, lastRow){
37785         if(dm.getRowCount()<1){
37786             this.fireEvent("beforerefresh", this);
37787             this.mainBody.update("");
37788             this.lockedBody.update("");
37789             this.fireEvent("refresh", this);
37790         }else{
37791             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
37792             var bt = this.getBodyTable();
37793             var tbody = bt.firstChild;
37794             var rows = bt.rows;
37795             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
37796                 tbody.removeChild(rows[firstRow]);
37797             }
37798             this.stripeRows(firstRow);
37799             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
37800         }
37801     },
37802
37803     updateRows : function(dataSource, firstRow, lastRow){
37804         var s = this.getScrollState();
37805         this.refresh();
37806         this.restoreScroll(s);
37807     },
37808
37809     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
37810         if(!noRefresh){
37811            this.refresh();
37812         }
37813         this.updateHeaderSortState();
37814     },
37815
37816     getScrollState : function(){
37817         
37818         var sb = this.scroller.dom;
37819         return {left: sb.scrollLeft, top: sb.scrollTop};
37820     },
37821
37822     stripeRows : function(startRow){
37823         if(!this.grid.stripeRows || this.ds.getCount() < 1){
37824             return;
37825         }
37826         startRow = startRow || 0;
37827         var rows = this.getBodyTable().rows;
37828         var lrows = this.getLockedTable().rows;
37829         var cls = ' x-grid-row-alt ';
37830         for(var i = startRow, len = rows.length; i < len; i++){
37831             var row = rows[i], lrow = lrows[i];
37832             var isAlt = ((i+1) % 2 == 0);
37833             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
37834             if(isAlt == hasAlt){
37835                 continue;
37836             }
37837             if(isAlt){
37838                 row.className += " x-grid-row-alt";
37839             }else{
37840                 row.className = row.className.replace("x-grid-row-alt", "");
37841             }
37842             if(lrow){
37843                 lrow.className = row.className;
37844             }
37845         }
37846     },
37847
37848     restoreScroll : function(state){
37849         //Roo.log('GridView.restoreScroll');
37850         var sb = this.scroller.dom;
37851         sb.scrollLeft = state.left;
37852         sb.scrollTop = state.top;
37853         this.syncScroll();
37854     },
37855
37856     syncScroll : function(){
37857         //Roo.log('GridView.syncScroll');
37858         var sb = this.scroller.dom;
37859         var sh = this.mainHd.dom;
37860         var bs = this.mainBody.dom;
37861         var lv = this.lockedBody.dom;
37862         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
37863         lv.scrollTop = bs.scrollTop = sb.scrollTop;
37864     },
37865
37866     handleScroll : function(e){
37867         this.syncScroll();
37868         var sb = this.scroller.dom;
37869         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
37870         e.stopEvent();
37871     },
37872
37873     handleWheel : function(e){
37874         var d = e.getWheelDelta();
37875         this.scroller.dom.scrollTop -= d*22;
37876         // set this here to prevent jumpy scrolling on large tables
37877         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
37878         e.stopEvent();
37879     },
37880
37881     renderRows : function(startRow, endRow){
37882         // pull in all the crap needed to render rows
37883         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
37884         var colCount = cm.getColumnCount();
37885
37886         if(ds.getCount() < 1){
37887             return ["", ""];
37888         }
37889
37890         // build a map for all the columns
37891         var cs = [];
37892         for(var i = 0; i < colCount; i++){
37893             var name = cm.getDataIndex(i);
37894             cs[i] = {
37895                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
37896                 renderer : cm.getRenderer(i),
37897                 id : cm.getColumnId(i),
37898                 locked : cm.isLocked(i),
37899                 has_editor : cm.isCellEditable(i)
37900             };
37901         }
37902
37903         startRow = startRow || 0;
37904         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
37905
37906         // records to render
37907         var rs = ds.getRange(startRow, endRow);
37908
37909         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
37910     },
37911
37912     // As much as I hate to duplicate code, this was branched because FireFox really hates
37913     // [].join("") on strings. The performance difference was substantial enough to
37914     // branch this function
37915     doRender : Roo.isGecko ?
37916             function(cs, rs, ds, startRow, colCount, stripe){
37917                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37918                 // buffers
37919                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37920                 
37921                 var hasListener = this.grid.hasListener('rowclass');
37922                 var rowcfg = {};
37923                 for(var j = 0, len = rs.length; j < len; j++){
37924                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
37925                     for(var i = 0; i < colCount; i++){
37926                         c = cs[i];
37927                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37928                         p.id = c.id;
37929                         p.css = p.attr = "";
37930                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37931                         if(p.value == undefined || p.value === "") {
37932                             p.value = "&#160;";
37933                         }
37934                         if(c.has_editor){
37935                             p.css += ' x-grid-editable-cell';
37936                         }
37937                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
37938                             p.css +=  ' x-grid-dirty-cell';
37939                         }
37940                         var markup = ct.apply(p);
37941                         if(!c.locked){
37942                             cb+= markup;
37943                         }else{
37944                             lcb+= markup;
37945                         }
37946                     }
37947                     var alt = [];
37948                     if(stripe && ((rowIndex+1) % 2 == 0)){
37949                         alt.push("x-grid-row-alt")
37950                     }
37951                     if(r.dirty){
37952                         alt.push(  " x-grid-dirty-row");
37953                     }
37954                     rp.cells = lcb;
37955                     if(this.getRowClass){
37956                         alt.push(this.getRowClass(r, rowIndex));
37957                     }
37958                     if (hasListener) {
37959                         rowcfg = {
37960                              
37961                             record: r,
37962                             rowIndex : rowIndex,
37963                             rowClass : ''
37964                         };
37965                         this.grid.fireEvent('rowclass', this, rowcfg);
37966                         alt.push(rowcfg.rowClass);
37967                     }
37968                     rp.alt = alt.join(" ");
37969                     lbuf+= rt.apply(rp);
37970                     rp.cells = cb;
37971                     buf+=  rt.apply(rp);
37972                 }
37973                 return [lbuf, buf];
37974             } :
37975             function(cs, rs, ds, startRow, colCount, stripe){
37976                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37977                 // buffers
37978                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37979                 var hasListener = this.grid.hasListener('rowclass');
37980  
37981                 var rowcfg = {};
37982                 for(var j = 0, len = rs.length; j < len; j++){
37983                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
37984                     for(var i = 0; i < colCount; i++){
37985                         c = cs[i];
37986                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37987                         p.id = c.id;
37988                         p.css = p.attr = "";
37989                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37990                         if(p.value == undefined || p.value === "") {
37991                             p.value = "&#160;";
37992                         }
37993                         //Roo.log(c);
37994                          if(c.has_editor){
37995                             p.css += ' x-grid-editable-cell';
37996                         }
37997                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37998                             p.css += ' x-grid-dirty-cell' 
37999                         }
38000                         
38001                         var markup = ct.apply(p);
38002                         if(!c.locked){
38003                             cb[cb.length] = markup;
38004                         }else{
38005                             lcb[lcb.length] = markup;
38006                         }
38007                     }
38008                     var alt = [];
38009                     if(stripe && ((rowIndex+1) % 2 == 0)){
38010                         alt.push( "x-grid-row-alt");
38011                     }
38012                     if(r.dirty){
38013                         alt.push(" x-grid-dirty-row");
38014                     }
38015                     rp.cells = lcb;
38016                     if(this.getRowClass){
38017                         alt.push( this.getRowClass(r, rowIndex));
38018                     }
38019                     if (hasListener) {
38020                         rowcfg = {
38021                              
38022                             record: r,
38023                             rowIndex : rowIndex,
38024                             rowClass : ''
38025                         };
38026                         this.grid.fireEvent('rowclass', this, rowcfg);
38027                         alt.push(rowcfg.rowClass);
38028                     }
38029                     
38030                     rp.alt = alt.join(" ");
38031                     rp.cells = lcb.join("");
38032                     lbuf[lbuf.length] = rt.apply(rp);
38033                     rp.cells = cb.join("");
38034                     buf[buf.length] =  rt.apply(rp);
38035                 }
38036                 return [lbuf.join(""), buf.join("")];
38037             },
38038
38039     renderBody : function(){
38040         var markup = this.renderRows();
38041         var bt = this.templates.body;
38042         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
38043     },
38044
38045     /**
38046      * Refreshes the grid
38047      * @param {Boolean} headersToo
38048      */
38049     refresh : function(headersToo){
38050         this.fireEvent("beforerefresh", this);
38051         this.grid.stopEditing();
38052         var result = this.renderBody();
38053         this.lockedBody.update(result[0]);
38054         this.mainBody.update(result[1]);
38055         if(headersToo === true){
38056             this.updateHeaders();
38057             this.updateColumns();
38058             this.updateSplitters();
38059             this.updateHeaderSortState();
38060         }
38061         this.syncRowHeights();
38062         this.layout();
38063         this.fireEvent("refresh", this);
38064     },
38065
38066     handleColumnMove : function(cm, oldIndex, newIndex){
38067         this.indexMap = null;
38068         var s = this.getScrollState();
38069         this.refresh(true);
38070         this.restoreScroll(s);
38071         this.afterMove(newIndex);
38072     },
38073
38074     afterMove : function(colIndex){
38075         if(this.enableMoveAnim && Roo.enableFx){
38076             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
38077         }
38078         // if multisort - fix sortOrder, and reload..
38079         if (this.grid.dataSource.multiSort) {
38080             // the we can call sort again..
38081             var dm = this.grid.dataSource;
38082             var cm = this.grid.colModel;
38083             var so = [];
38084             for(var i = 0; i < cm.config.length; i++ ) {
38085                 
38086                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
38087                     continue; // dont' bother, it's not in sort list or being set.
38088                 }
38089                 
38090                 so.push(cm.config[i].dataIndex);
38091             };
38092             dm.sortOrder = so;
38093             dm.load(dm.lastOptions);
38094             
38095             
38096         }
38097         
38098     },
38099
38100     updateCell : function(dm, rowIndex, dataIndex){
38101         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
38102         if(typeof colIndex == "undefined"){ // not present in grid
38103             return;
38104         }
38105         var cm = this.grid.colModel;
38106         var cell = this.getCell(rowIndex, colIndex);
38107         var cellText = this.getCellText(rowIndex, colIndex);
38108
38109         var p = {
38110             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
38111             id : cm.getColumnId(colIndex),
38112             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
38113         };
38114         var renderer = cm.getRenderer(colIndex);
38115         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
38116         if(typeof val == "undefined" || val === "") {
38117             val = "&#160;";
38118         }
38119         cellText.innerHTML = val;
38120         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
38121         this.syncRowHeights(rowIndex, rowIndex);
38122     },
38123
38124     calcColumnWidth : function(colIndex, maxRowsToMeasure){
38125         var maxWidth = 0;
38126         if(this.grid.autoSizeHeaders){
38127             var h = this.getHeaderCellMeasure(colIndex);
38128             maxWidth = Math.max(maxWidth, h.scrollWidth);
38129         }
38130         var tb, index;
38131         if(this.cm.isLocked(colIndex)){
38132             tb = this.getLockedTable();
38133             index = colIndex;
38134         }else{
38135             tb = this.getBodyTable();
38136             index = colIndex - this.cm.getLockedCount();
38137         }
38138         if(tb && tb.rows){
38139             var rows = tb.rows;
38140             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
38141             for(var i = 0; i < stopIndex; i++){
38142                 var cell = rows[i].childNodes[index].firstChild;
38143                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
38144             }
38145         }
38146         return maxWidth + /*margin for error in IE*/ 5;
38147     },
38148     /**
38149      * Autofit a column to its content.
38150      * @param {Number} colIndex
38151      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
38152      */
38153      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
38154          if(this.cm.isHidden(colIndex)){
38155              return; // can't calc a hidden column
38156          }
38157         if(forceMinSize){
38158             var cid = this.cm.getColumnId(colIndex);
38159             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
38160            if(this.grid.autoSizeHeaders){
38161                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
38162            }
38163         }
38164         var newWidth = this.calcColumnWidth(colIndex);
38165         this.cm.setColumnWidth(colIndex,
38166             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
38167         if(!suppressEvent){
38168             this.grid.fireEvent("columnresize", colIndex, newWidth);
38169         }
38170     },
38171
38172     /**
38173      * Autofits all columns to their content and then expands to fit any extra space in the grid
38174      */
38175      autoSizeColumns : function(){
38176         var cm = this.grid.colModel;
38177         var colCount = cm.getColumnCount();
38178         for(var i = 0; i < colCount; i++){
38179             this.autoSizeColumn(i, true, true);
38180         }
38181         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
38182             this.fitColumns();
38183         }else{
38184             this.updateColumns();
38185             this.layout();
38186         }
38187     },
38188
38189     /**
38190      * Autofits all columns to the grid's width proportionate with their current size
38191      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
38192      */
38193     fitColumns : function(reserveScrollSpace){
38194         var cm = this.grid.colModel;
38195         var colCount = cm.getColumnCount();
38196         var cols = [];
38197         var width = 0;
38198         var i, w;
38199         for (i = 0; i < colCount; i++){
38200             if(!cm.isHidden(i) && !cm.isFixed(i)){
38201                 w = cm.getColumnWidth(i);
38202                 cols.push(i);
38203                 cols.push(w);
38204                 width += w;
38205             }
38206         }
38207         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
38208         if(reserveScrollSpace){
38209             avail -= 17;
38210         }
38211         var frac = (avail - cm.getTotalWidth())/width;
38212         while (cols.length){
38213             w = cols.pop();
38214             i = cols.pop();
38215             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
38216         }
38217         this.updateColumns();
38218         this.layout();
38219     },
38220
38221     onRowSelect : function(rowIndex){
38222         var row = this.getRowComposite(rowIndex);
38223         row.addClass("x-grid-row-selected");
38224     },
38225
38226     onRowDeselect : function(rowIndex){
38227         var row = this.getRowComposite(rowIndex);
38228         row.removeClass("x-grid-row-selected");
38229     },
38230
38231     onCellSelect : function(row, col){
38232         var cell = this.getCell(row, col);
38233         if(cell){
38234             Roo.fly(cell).addClass("x-grid-cell-selected");
38235         }
38236     },
38237
38238     onCellDeselect : function(row, col){
38239         var cell = this.getCell(row, col);
38240         if(cell){
38241             Roo.fly(cell).removeClass("x-grid-cell-selected");
38242         }
38243     },
38244
38245     updateHeaderSortState : function(){
38246         
38247         // sort state can be single { field: xxx, direction : yyy}
38248         // or   { xxx=>ASC , yyy : DESC ..... }
38249         
38250         var mstate = {};
38251         if (!this.ds.multiSort) { 
38252             var state = this.ds.getSortState();
38253             if(!state){
38254                 return;
38255             }
38256             mstate[state.field] = state.direction;
38257             // FIXME... - this is not used here.. but might be elsewhere..
38258             this.sortState = state;
38259             
38260         } else {
38261             mstate = this.ds.sortToggle;
38262         }
38263         //remove existing sort classes..
38264         
38265         var sc = this.sortClasses;
38266         var hds = this.el.select(this.headerSelector).removeClass(sc);
38267         
38268         for(var f in mstate) {
38269         
38270             var sortColumn = this.cm.findColumnIndex(f);
38271             
38272             if(sortColumn != -1){
38273                 var sortDir = mstate[f];        
38274                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
38275             }
38276         }
38277         
38278          
38279         
38280     },
38281
38282
38283     handleHeaderClick : function(g, index,e){
38284         
38285         Roo.log("header click");
38286         
38287         if (Roo.isTouch) {
38288             // touch events on header are handled by context
38289             this.handleHdCtx(g,index,e);
38290             return;
38291         }
38292         
38293         
38294         if(this.headersDisabled){
38295             return;
38296         }
38297         var dm = g.dataSource, cm = g.colModel;
38298         if(!cm.isSortable(index)){
38299             return;
38300         }
38301         g.stopEditing();
38302         
38303         if (dm.multiSort) {
38304             // update the sortOrder
38305             var so = [];
38306             for(var i = 0; i < cm.config.length; i++ ) {
38307                 
38308                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
38309                     continue; // dont' bother, it's not in sort list or being set.
38310                 }
38311                 
38312                 so.push(cm.config[i].dataIndex);
38313             };
38314             dm.sortOrder = so;
38315         }
38316         
38317         
38318         dm.sort(cm.getDataIndex(index));
38319     },
38320
38321
38322     destroy : function(){
38323         if(this.colMenu){
38324             this.colMenu.removeAll();
38325             Roo.menu.MenuMgr.unregister(this.colMenu);
38326             this.colMenu.getEl().remove();
38327             delete this.colMenu;
38328         }
38329         if(this.hmenu){
38330             this.hmenu.removeAll();
38331             Roo.menu.MenuMgr.unregister(this.hmenu);
38332             this.hmenu.getEl().remove();
38333             delete this.hmenu;
38334         }
38335         if(this.grid.enableColumnMove){
38336             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
38337             if(dds){
38338                 for(var dd in dds){
38339                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
38340                         var elid = dds[dd].dragElId;
38341                         dds[dd].unreg();
38342                         Roo.get(elid).remove();
38343                     } else if(dds[dd].config.isTarget){
38344                         dds[dd].proxyTop.remove();
38345                         dds[dd].proxyBottom.remove();
38346                         dds[dd].unreg();
38347                     }
38348                     if(Roo.dd.DDM.locationCache[dd]){
38349                         delete Roo.dd.DDM.locationCache[dd];
38350                     }
38351                 }
38352                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
38353             }
38354         }
38355         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
38356         this.bind(null, null);
38357         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
38358     },
38359
38360     handleLockChange : function(){
38361         this.refresh(true);
38362     },
38363
38364     onDenyColumnLock : function(){
38365
38366     },
38367
38368     onDenyColumnHide : function(){
38369
38370     },
38371
38372     handleHdMenuClick : function(item){
38373         var index = this.hdCtxIndex;
38374         var cm = this.cm, ds = this.ds;
38375         switch(item.id){
38376             case "asc":
38377                 ds.sort(cm.getDataIndex(index), "ASC");
38378                 break;
38379             case "desc":
38380                 ds.sort(cm.getDataIndex(index), "DESC");
38381                 break;
38382             case "lock":
38383                 var lc = cm.getLockedCount();
38384                 if(cm.getColumnCount(true) <= lc+1){
38385                     this.onDenyColumnLock();
38386                     return;
38387                 }
38388                 if(lc != index){
38389                     cm.setLocked(index, true, true);
38390                     cm.moveColumn(index, lc);
38391                     this.grid.fireEvent("columnmove", index, lc);
38392                 }else{
38393                     cm.setLocked(index, true);
38394                 }
38395             break;
38396             case "unlock":
38397                 var lc = cm.getLockedCount();
38398                 if((lc-1) != index){
38399                     cm.setLocked(index, false, true);
38400                     cm.moveColumn(index, lc-1);
38401                     this.grid.fireEvent("columnmove", index, lc-1);
38402                 }else{
38403                     cm.setLocked(index, false);
38404                 }
38405             break;
38406             case 'wider': // used to expand cols on touch..
38407             case 'narrow':
38408                 var cw = cm.getColumnWidth(index);
38409                 cw += (item.id == 'wider' ? 1 : -1) * 50;
38410                 cw = Math.max(0, cw);
38411                 cw = Math.min(cw,4000);
38412                 cm.setColumnWidth(index, cw);
38413                 break;
38414                 
38415             default:
38416                 index = cm.getIndexById(item.id.substr(4));
38417                 if(index != -1){
38418                     if(item.checked && cm.getColumnCount(true) <= 1){
38419                         this.onDenyColumnHide();
38420                         return false;
38421                     }
38422                     cm.setHidden(index, item.checked);
38423                 }
38424         }
38425         return true;
38426     },
38427
38428     beforeColMenuShow : function(){
38429         var cm = this.cm,  colCount = cm.getColumnCount();
38430         this.colMenu.removeAll();
38431         for(var i = 0; i < colCount; i++){
38432             this.colMenu.add(new Roo.menu.CheckItem({
38433                 id: "col-"+cm.getColumnId(i),
38434                 text: cm.getColumnHeader(i),
38435                 checked: !cm.isHidden(i),
38436                 hideOnClick:false
38437             }));
38438         }
38439     },
38440
38441     handleHdCtx : function(g, index, e){
38442         e.stopEvent();
38443         var hd = this.getHeaderCell(index);
38444         this.hdCtxIndex = index;
38445         var ms = this.hmenu.items, cm = this.cm;
38446         ms.get("asc").setDisabled(!cm.isSortable(index));
38447         ms.get("desc").setDisabled(!cm.isSortable(index));
38448         if(this.grid.enableColLock !== false){
38449             ms.get("lock").setDisabled(cm.isLocked(index));
38450             ms.get("unlock").setDisabled(!cm.isLocked(index));
38451         }
38452         this.hmenu.show(hd, "tl-bl");
38453     },
38454
38455     handleHdOver : function(e){
38456         var hd = this.findHeaderCell(e.getTarget());
38457         if(hd && !this.headersDisabled){
38458             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
38459                this.fly(hd).addClass("x-grid-hd-over");
38460             }
38461         }
38462     },
38463
38464     handleHdOut : function(e){
38465         var hd = this.findHeaderCell(e.getTarget());
38466         if(hd){
38467             this.fly(hd).removeClass("x-grid-hd-over");
38468         }
38469     },
38470
38471     handleSplitDblClick : function(e, t){
38472         var i = this.getCellIndex(t);
38473         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
38474             this.autoSizeColumn(i, true);
38475             this.layout();
38476         }
38477     },
38478
38479     render : function(){
38480
38481         var cm = this.cm;
38482         var colCount = cm.getColumnCount();
38483
38484         if(this.grid.monitorWindowResize === true){
38485             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38486         }
38487         var header = this.renderHeaders();
38488         var body = this.templates.body.apply({rows:""});
38489         var html = this.templates.master.apply({
38490             lockedBody: body,
38491             body: body,
38492             lockedHeader: header[0],
38493             header: header[1]
38494         });
38495
38496         //this.updateColumns();
38497
38498         this.grid.getGridEl().dom.innerHTML = html;
38499
38500         this.initElements();
38501         
38502         // a kludge to fix the random scolling effect in webkit
38503         this.el.on("scroll", function() {
38504             this.el.dom.scrollTop=0; // hopefully not recursive..
38505         },this);
38506
38507         this.scroller.on("scroll", this.handleScroll, this);
38508         this.lockedBody.on("mousewheel", this.handleWheel, this);
38509         this.mainBody.on("mousewheel", this.handleWheel, this);
38510
38511         this.mainHd.on("mouseover", this.handleHdOver, this);
38512         this.mainHd.on("mouseout", this.handleHdOut, this);
38513         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
38514                 {delegate: "."+this.splitClass});
38515
38516         this.lockedHd.on("mouseover", this.handleHdOver, this);
38517         this.lockedHd.on("mouseout", this.handleHdOut, this);
38518         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
38519                 {delegate: "."+this.splitClass});
38520
38521         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
38522             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38523         }
38524
38525         this.updateSplitters();
38526
38527         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
38528             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38529             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38530         }
38531
38532         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
38533             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
38534             this.hmenu.add(
38535                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
38536                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
38537             );
38538             if(this.grid.enableColLock !== false){
38539                 this.hmenu.add('-',
38540                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
38541                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
38542                 );
38543             }
38544             if (Roo.isTouch) {
38545                  this.hmenu.add('-',
38546                     {id:"wider", text: this.columnsWiderText},
38547                     {id:"narrow", text: this.columnsNarrowText }
38548                 );
38549                 
38550                  
38551             }
38552             
38553             if(this.grid.enableColumnHide !== false){
38554
38555                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
38556                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
38557                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
38558
38559                 this.hmenu.add('-',
38560                     {id:"columns", text: this.columnsText, menu: this.colMenu}
38561                 );
38562             }
38563             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
38564
38565             this.grid.on("headercontextmenu", this.handleHdCtx, this);
38566         }
38567
38568         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
38569             this.dd = new Roo.grid.GridDragZone(this.grid, {
38570                 ddGroup : this.grid.ddGroup || 'GridDD'
38571             });
38572             
38573         }
38574
38575         /*
38576         for(var i = 0; i < colCount; i++){
38577             if(cm.isHidden(i)){
38578                 this.hideColumn(i);
38579             }
38580             if(cm.config[i].align){
38581                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
38582                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
38583             }
38584         }*/
38585         
38586         this.updateHeaderSortState();
38587
38588         this.beforeInitialResize();
38589         this.layout(true);
38590
38591         // two part rendering gives faster view to the user
38592         this.renderPhase2.defer(1, this);
38593     },
38594
38595     renderPhase2 : function(){
38596         // render the rows now
38597         this.refresh();
38598         if(this.grid.autoSizeColumns){
38599             this.autoSizeColumns();
38600         }
38601     },
38602
38603     beforeInitialResize : function(){
38604
38605     },
38606
38607     onColumnSplitterMoved : function(i, w){
38608         this.userResized = true;
38609         var cm = this.grid.colModel;
38610         cm.setColumnWidth(i, w, true);
38611         var cid = cm.getColumnId(i);
38612         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
38613         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
38614         this.updateSplitters();
38615         this.layout();
38616         this.grid.fireEvent("columnresize", i, w);
38617     },
38618
38619     syncRowHeights : function(startIndex, endIndex){
38620         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
38621             startIndex = startIndex || 0;
38622             var mrows = this.getBodyTable().rows;
38623             var lrows = this.getLockedTable().rows;
38624             var len = mrows.length-1;
38625             endIndex = Math.min(endIndex || len, len);
38626             for(var i = startIndex; i <= endIndex; i++){
38627                 var m = mrows[i], l = lrows[i];
38628                 var h = Math.max(m.offsetHeight, l.offsetHeight);
38629                 m.style.height = l.style.height = h + "px";
38630             }
38631         }
38632     },
38633
38634     layout : function(initialRender, is2ndPass){
38635         var g = this.grid;
38636         var auto = g.autoHeight;
38637         var scrollOffset = 16;
38638         var c = g.getGridEl(), cm = this.cm,
38639                 expandCol = g.autoExpandColumn,
38640                 gv = this;
38641         //c.beginMeasure();
38642
38643         if(!c.dom.offsetWidth){ // display:none?
38644             if(initialRender){
38645                 this.lockedWrap.show();
38646                 this.mainWrap.show();
38647             }
38648             return;
38649         }
38650
38651         var hasLock = this.cm.isLocked(0);
38652
38653         var tbh = this.headerPanel.getHeight();
38654         var bbh = this.footerPanel.getHeight();
38655
38656         if(auto){
38657             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
38658             var newHeight = ch + c.getBorderWidth("tb");
38659             if(g.maxHeight){
38660                 newHeight = Math.min(g.maxHeight, newHeight);
38661             }
38662             c.setHeight(newHeight);
38663         }
38664
38665         if(g.autoWidth){
38666             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
38667         }
38668
38669         var s = this.scroller;
38670
38671         var csize = c.getSize(true);
38672
38673         this.el.setSize(csize.width, csize.height);
38674
38675         this.headerPanel.setWidth(csize.width);
38676         this.footerPanel.setWidth(csize.width);
38677
38678         var hdHeight = this.mainHd.getHeight();
38679         var vw = csize.width;
38680         var vh = csize.height - (tbh + bbh);
38681
38682         s.setSize(vw, vh);
38683
38684         var bt = this.getBodyTable();
38685         
38686         if(cm.getLockedCount() == cm.config.length){
38687             bt = this.getLockedTable();
38688         }
38689         
38690         var ltWidth = hasLock ?
38691                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
38692
38693         var scrollHeight = bt.offsetHeight;
38694         var scrollWidth = ltWidth + bt.offsetWidth;
38695         var vscroll = false, hscroll = false;
38696
38697         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
38698
38699         var lw = this.lockedWrap, mw = this.mainWrap;
38700         var lb = this.lockedBody, mb = this.mainBody;
38701
38702         setTimeout(function(){
38703             var t = s.dom.offsetTop;
38704             var w = s.dom.clientWidth,
38705                 h = s.dom.clientHeight;
38706
38707             lw.setTop(t);
38708             lw.setSize(ltWidth, h);
38709
38710             mw.setLeftTop(ltWidth, t);
38711             mw.setSize(w-ltWidth, h);
38712
38713             lb.setHeight(h-hdHeight);
38714             mb.setHeight(h-hdHeight);
38715
38716             if(is2ndPass !== true && !gv.userResized && expandCol){
38717                 // high speed resize without full column calculation
38718                 
38719                 var ci = cm.getIndexById(expandCol);
38720                 if (ci < 0) {
38721                     ci = cm.findColumnIndex(expandCol);
38722                 }
38723                 ci = Math.max(0, ci); // make sure it's got at least the first col.
38724                 var expandId = cm.getColumnId(ci);
38725                 var  tw = cm.getTotalWidth(false);
38726                 var currentWidth = cm.getColumnWidth(ci);
38727                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
38728                 if(currentWidth != cw){
38729                     cm.setColumnWidth(ci, cw, true);
38730                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38731                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38732                     gv.updateSplitters();
38733                     gv.layout(false, true);
38734                 }
38735             }
38736
38737             if(initialRender){
38738                 lw.show();
38739                 mw.show();
38740             }
38741             //c.endMeasure();
38742         }, 10);
38743     },
38744
38745     onWindowResize : function(){
38746         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
38747             return;
38748         }
38749         this.layout();
38750     },
38751
38752     appendFooter : function(parentEl){
38753         return null;
38754     },
38755
38756     sortAscText : "Sort Ascending",
38757     sortDescText : "Sort Descending",
38758     lockText : "Lock Column",
38759     unlockText : "Unlock Column",
38760     columnsText : "Columns",
38761  
38762     columnsWiderText : "Wider",
38763     columnsNarrowText : "Thinner"
38764 });
38765
38766
38767 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
38768     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
38769     this.proxy.el.addClass('x-grid3-col-dd');
38770 };
38771
38772 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
38773     handleMouseDown : function(e){
38774
38775     },
38776
38777     callHandleMouseDown : function(e){
38778         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
38779     }
38780 });
38781 /*
38782  * Based on:
38783  * Ext JS Library 1.1.1
38784  * Copyright(c) 2006-2007, Ext JS, LLC.
38785  *
38786  * Originally Released Under LGPL - original licence link has changed is not relivant.
38787  *
38788  * Fork - LGPL
38789  * <script type="text/javascript">
38790  */
38791  
38792 // private
38793 // This is a support class used internally by the Grid components
38794 Roo.grid.SplitDragZone = function(grid, hd, hd2){
38795     this.grid = grid;
38796     this.view = grid.getView();
38797     this.proxy = this.view.resizeProxy;
38798     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
38799         "gridSplitters" + this.grid.getGridEl().id, {
38800         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
38801     });
38802     this.setHandleElId(Roo.id(hd));
38803     this.setOuterHandleElId(Roo.id(hd2));
38804     this.scroll = false;
38805 };
38806 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
38807     fly: Roo.Element.fly,
38808
38809     b4StartDrag : function(x, y){
38810         this.view.headersDisabled = true;
38811         this.proxy.setHeight(this.view.mainWrap.getHeight());
38812         var w = this.cm.getColumnWidth(this.cellIndex);
38813         var minw = Math.max(w-this.grid.minColumnWidth, 0);
38814         this.resetConstraints();
38815         this.setXConstraint(minw, 1000);
38816         this.setYConstraint(0, 0);
38817         this.minX = x - minw;
38818         this.maxX = x + 1000;
38819         this.startPos = x;
38820         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
38821     },
38822
38823
38824     handleMouseDown : function(e){
38825         ev = Roo.EventObject.setEvent(e);
38826         var t = this.fly(ev.getTarget());
38827         if(t.hasClass("x-grid-split")){
38828             this.cellIndex = this.view.getCellIndex(t.dom);
38829             this.split = t.dom;
38830             this.cm = this.grid.colModel;
38831             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
38832                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
38833             }
38834         }
38835     },
38836
38837     endDrag : function(e){
38838         this.view.headersDisabled = false;
38839         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
38840         var diff = endX - this.startPos;
38841         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
38842     },
38843
38844     autoOffset : function(){
38845         this.setDelta(0,0);
38846     }
38847 });/*
38848  * Based on:
38849  * Ext JS Library 1.1.1
38850  * Copyright(c) 2006-2007, Ext JS, LLC.
38851  *
38852  * Originally Released Under LGPL - original licence link has changed is not relivant.
38853  *
38854  * Fork - LGPL
38855  * <script type="text/javascript">
38856  */
38857  
38858 // private
38859 // This is a support class used internally by the Grid components
38860 Roo.grid.GridDragZone = function(grid, config){
38861     this.view = grid.getView();
38862     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
38863     if(this.view.lockedBody){
38864         this.setHandleElId(Roo.id(this.view.mainBody.dom));
38865         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
38866     }
38867     this.scroll = false;
38868     this.grid = grid;
38869     this.ddel = document.createElement('div');
38870     this.ddel.className = 'x-grid-dd-wrap';
38871 };
38872
38873 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
38874     ddGroup : "GridDD",
38875
38876     getDragData : function(e){
38877         var t = Roo.lib.Event.getTarget(e);
38878         var rowIndex = this.view.findRowIndex(t);
38879         var sm = this.grid.selModel;
38880             
38881         //Roo.log(rowIndex);
38882         
38883         if (sm.getSelectedCell) {
38884             // cell selection..
38885             if (!sm.getSelectedCell()) {
38886                 return false;
38887             }
38888             if (rowIndex != sm.getSelectedCell()[0]) {
38889                 return false;
38890             }
38891         
38892         }
38893         
38894         if(rowIndex !== false){
38895             
38896             // if editorgrid.. 
38897             
38898             
38899             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
38900                
38901             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
38902               //  
38903             //}
38904             if (e.hasModifier()){
38905                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
38906             }
38907             
38908             Roo.log("getDragData");
38909             
38910             return {
38911                 grid: this.grid,
38912                 ddel: this.ddel,
38913                 rowIndex: rowIndex,
38914                 selections:sm.getSelections ? sm.getSelections() : (
38915                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
38916                 )
38917             };
38918         }
38919         return false;
38920     },
38921
38922     onInitDrag : function(e){
38923         var data = this.dragData;
38924         this.ddel.innerHTML = this.grid.getDragDropText();
38925         this.proxy.update(this.ddel);
38926         // fire start drag?
38927     },
38928
38929     afterRepair : function(){
38930         this.dragging = false;
38931     },
38932
38933     getRepairXY : function(e, data){
38934         return false;
38935     },
38936
38937     onEndDrag : function(data, e){
38938         // fire end drag?
38939     },
38940
38941     onValidDrop : function(dd, e, id){
38942         // fire drag drop?
38943         this.hideProxy();
38944     },
38945
38946     beforeInvalidDrop : function(e, id){
38947
38948     }
38949 });/*
38950  * Based on:
38951  * Ext JS Library 1.1.1
38952  * Copyright(c) 2006-2007, Ext JS, LLC.
38953  *
38954  * Originally Released Under LGPL - original licence link has changed is not relivant.
38955  *
38956  * Fork - LGPL
38957  * <script type="text/javascript">
38958  */
38959  
38960
38961 /**
38962  * @class Roo.grid.ColumnModel
38963  * @extends Roo.util.Observable
38964  * This is the default implementation of a ColumnModel used by the Grid. It defines
38965  * the columns in the grid.
38966  * <br>Usage:<br>
38967  <pre><code>
38968  var colModel = new Roo.grid.ColumnModel([
38969         {header: "Ticker", width: 60, sortable: true, locked: true},
38970         {header: "Company Name", width: 150, sortable: true},
38971         {header: "Market Cap.", width: 100, sortable: true},
38972         {header: "$ Sales", width: 100, sortable: true, renderer: money},
38973         {header: "Employees", width: 100, sortable: true, resizable: false}
38974  ]);
38975  </code></pre>
38976  * <p>
38977  
38978  * The config options listed for this class are options which may appear in each
38979  * individual column definition.
38980  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
38981  * @constructor
38982  * @param {Object} config An Array of column config objects. See this class's
38983  * config objects for details.
38984 */
38985 Roo.grid.ColumnModel = function(config){
38986         /**
38987      * The config passed into the constructor
38988      */
38989     this.config = config;
38990     this.lookup = {};
38991
38992     // if no id, create one
38993     // if the column does not have a dataIndex mapping,
38994     // map it to the order it is in the config
38995     for(var i = 0, len = config.length; i < len; i++){
38996         var c = config[i];
38997         if(typeof c.dataIndex == "undefined"){
38998             c.dataIndex = i;
38999         }
39000         if(typeof c.renderer == "string"){
39001             c.renderer = Roo.util.Format[c.renderer];
39002         }
39003         if(typeof c.id == "undefined"){
39004             c.id = Roo.id();
39005         }
39006         if(c.editor && c.editor.xtype){
39007             c.editor  = Roo.factory(c.editor, Roo.grid);
39008         }
39009         if(c.editor && c.editor.isFormField){
39010             c.editor = new Roo.grid.GridEditor(c.editor);
39011         }
39012         this.lookup[c.id] = c;
39013     }
39014
39015     /**
39016      * The width of columns which have no width specified (defaults to 100)
39017      * @type Number
39018      */
39019     this.defaultWidth = 100;
39020
39021     /**
39022      * Default sortable of columns which have no sortable specified (defaults to false)
39023      * @type Boolean
39024      */
39025     this.defaultSortable = false;
39026
39027     this.addEvents({
39028         /**
39029              * @event widthchange
39030              * Fires when the width of a column changes.
39031              * @param {ColumnModel} this
39032              * @param {Number} columnIndex The column index
39033              * @param {Number} newWidth The new width
39034              */
39035             "widthchange": true,
39036         /**
39037              * @event headerchange
39038              * Fires when the text of a header changes.
39039              * @param {ColumnModel} this
39040              * @param {Number} columnIndex The column index
39041              * @param {Number} newText The new header text
39042              */
39043             "headerchange": true,
39044         /**
39045              * @event hiddenchange
39046              * Fires when a column is hidden or "unhidden".
39047              * @param {ColumnModel} this
39048              * @param {Number} columnIndex The column index
39049              * @param {Boolean} hidden true if hidden, false otherwise
39050              */
39051             "hiddenchange": true,
39052             /**
39053          * @event columnmoved
39054          * Fires when a column is moved.
39055          * @param {ColumnModel} this
39056          * @param {Number} oldIndex
39057          * @param {Number} newIndex
39058          */
39059         "columnmoved" : true,
39060         /**
39061          * @event columlockchange
39062          * Fires when a column's locked state is changed
39063          * @param {ColumnModel} this
39064          * @param {Number} colIndex
39065          * @param {Boolean} locked true if locked
39066          */
39067         "columnlockchange" : true
39068     });
39069     Roo.grid.ColumnModel.superclass.constructor.call(this);
39070 };
39071 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
39072     /**
39073      * @cfg {String} header The header text to display in the Grid view.
39074      */
39075     /**
39076      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
39077      * {@link Roo.data.Record} definition from which to draw the column's value. If not
39078      * specified, the column's index is used as an index into the Record's data Array.
39079      */
39080     /**
39081      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
39082      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
39083      */
39084     /**
39085      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
39086      * Defaults to the value of the {@link #defaultSortable} property.
39087      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
39088      */
39089     /**
39090      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
39091      */
39092     /**
39093      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
39094      */
39095     /**
39096      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
39097      */
39098     /**
39099      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
39100      */
39101     /**
39102      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
39103      * given the cell's data value. See {@link #setRenderer}. If not specified, the
39104      * default renderer uses the raw data value. If an object is returned (bootstrap only)
39105      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
39106      */
39107        /**
39108      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
39109      */
39110     /**
39111      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
39112      */
39113     /**
39114      * @cfg {String} cursor (Optional)
39115      */
39116     /**
39117      * @cfg {String} tooltip (Optional)
39118      */
39119     /**
39120      * @cfg {Number} xs (Optional)
39121      */
39122     /**
39123      * @cfg {Number} sm (Optional)
39124      */
39125     /**
39126      * @cfg {Number} md (Optional)
39127      */
39128     /**
39129      * @cfg {Number} lg (Optional)
39130      */
39131     /**
39132      * Returns the id of the column at the specified index.
39133      * @param {Number} index The column index
39134      * @return {String} the id
39135      */
39136     getColumnId : function(index){
39137         return this.config[index].id;
39138     },
39139
39140     /**
39141      * Returns the column for a specified id.
39142      * @param {String} id The column id
39143      * @return {Object} the column
39144      */
39145     getColumnById : function(id){
39146         return this.lookup[id];
39147     },
39148
39149     
39150     /**
39151      * Returns the column for a specified dataIndex.
39152      * @param {String} dataIndex The column dataIndex
39153      * @return {Object|Boolean} the column or false if not found
39154      */
39155     getColumnByDataIndex: function(dataIndex){
39156         var index = this.findColumnIndex(dataIndex);
39157         return index > -1 ? this.config[index] : false;
39158     },
39159     
39160     /**
39161      * Returns the index for a specified column id.
39162      * @param {String} id The column id
39163      * @return {Number} the index, or -1 if not found
39164      */
39165     getIndexById : function(id){
39166         for(var i = 0, len = this.config.length; i < len; i++){
39167             if(this.config[i].id == id){
39168                 return i;
39169             }
39170         }
39171         return -1;
39172     },
39173     
39174     /**
39175      * Returns the index for a specified column dataIndex.
39176      * @param {String} dataIndex The column dataIndex
39177      * @return {Number} the index, or -1 if not found
39178      */
39179     
39180     findColumnIndex : function(dataIndex){
39181         for(var i = 0, len = this.config.length; i < len; i++){
39182             if(this.config[i].dataIndex == dataIndex){
39183                 return i;
39184             }
39185         }
39186         return -1;
39187     },
39188     
39189     
39190     moveColumn : function(oldIndex, newIndex){
39191         var c = this.config[oldIndex];
39192         this.config.splice(oldIndex, 1);
39193         this.config.splice(newIndex, 0, c);
39194         this.dataMap = null;
39195         this.fireEvent("columnmoved", this, oldIndex, newIndex);
39196     },
39197
39198     isLocked : function(colIndex){
39199         return this.config[colIndex].locked === true;
39200     },
39201
39202     setLocked : function(colIndex, value, suppressEvent){
39203         if(this.isLocked(colIndex) == value){
39204             return;
39205         }
39206         this.config[colIndex].locked = value;
39207         if(!suppressEvent){
39208             this.fireEvent("columnlockchange", this, colIndex, value);
39209         }
39210     },
39211
39212     getTotalLockedWidth : function(){
39213         var totalWidth = 0;
39214         for(var i = 0; i < this.config.length; i++){
39215             if(this.isLocked(i) && !this.isHidden(i)){
39216                 this.totalWidth += this.getColumnWidth(i);
39217             }
39218         }
39219         return totalWidth;
39220     },
39221
39222     getLockedCount : function(){
39223         for(var i = 0, len = this.config.length; i < len; i++){
39224             if(!this.isLocked(i)){
39225                 return i;
39226             }
39227         }
39228         
39229         return this.config.length;
39230     },
39231
39232     /**
39233      * Returns the number of columns.
39234      * @return {Number}
39235      */
39236     getColumnCount : function(visibleOnly){
39237         if(visibleOnly === true){
39238             var c = 0;
39239             for(var i = 0, len = this.config.length; i < len; i++){
39240                 if(!this.isHidden(i)){
39241                     c++;
39242                 }
39243             }
39244             return c;
39245         }
39246         return this.config.length;
39247     },
39248
39249     /**
39250      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
39251      * @param {Function} fn
39252      * @param {Object} scope (optional)
39253      * @return {Array} result
39254      */
39255     getColumnsBy : function(fn, scope){
39256         var r = [];
39257         for(var i = 0, len = this.config.length; i < len; i++){
39258             var c = this.config[i];
39259             if(fn.call(scope||this, c, i) === true){
39260                 r[r.length] = c;
39261             }
39262         }
39263         return r;
39264     },
39265
39266     /**
39267      * Returns true if the specified column is sortable.
39268      * @param {Number} col The column index
39269      * @return {Boolean}
39270      */
39271     isSortable : function(col){
39272         if(typeof this.config[col].sortable == "undefined"){
39273             return this.defaultSortable;
39274         }
39275         return this.config[col].sortable;
39276     },
39277
39278     /**
39279      * Returns the rendering (formatting) function defined for the column.
39280      * @param {Number} col The column index.
39281      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
39282      */
39283     getRenderer : function(col){
39284         if(!this.config[col].renderer){
39285             return Roo.grid.ColumnModel.defaultRenderer;
39286         }
39287         return this.config[col].renderer;
39288     },
39289
39290     /**
39291      * Sets the rendering (formatting) function for a column.
39292      * @param {Number} col The column index
39293      * @param {Function} fn The function to use to process the cell's raw data
39294      * to return HTML markup for the grid view. The render function is called with
39295      * the following parameters:<ul>
39296      * <li>Data value.</li>
39297      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
39298      * <li>css A CSS style string to apply to the table cell.</li>
39299      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
39300      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
39301      * <li>Row index</li>
39302      * <li>Column index</li>
39303      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
39304      */
39305     setRenderer : function(col, fn){
39306         this.config[col].renderer = fn;
39307     },
39308
39309     /**
39310      * Returns the width for the specified column.
39311      * @param {Number} col The column index
39312      * @return {Number}
39313      */
39314     getColumnWidth : function(col){
39315         return this.config[col].width * 1 || this.defaultWidth;
39316     },
39317
39318     /**
39319      * Sets the width for a column.
39320      * @param {Number} col The column index
39321      * @param {Number} width The new width
39322      */
39323     setColumnWidth : function(col, width, suppressEvent){
39324         this.config[col].width = width;
39325         this.totalWidth = null;
39326         if(!suppressEvent){
39327              this.fireEvent("widthchange", this, col, width);
39328         }
39329     },
39330
39331     /**
39332      * Returns the total width of all columns.
39333      * @param {Boolean} includeHidden True to include hidden column widths
39334      * @return {Number}
39335      */
39336     getTotalWidth : function(includeHidden){
39337         if(!this.totalWidth){
39338             this.totalWidth = 0;
39339             for(var i = 0, len = this.config.length; i < len; i++){
39340                 if(includeHidden || !this.isHidden(i)){
39341                     this.totalWidth += this.getColumnWidth(i);
39342                 }
39343             }
39344         }
39345         return this.totalWidth;
39346     },
39347
39348     /**
39349      * Returns the header for the specified column.
39350      * @param {Number} col The column index
39351      * @return {String}
39352      */
39353     getColumnHeader : function(col){
39354         return this.config[col].header;
39355     },
39356
39357     /**
39358      * Sets the header for a column.
39359      * @param {Number} col The column index
39360      * @param {String} header The new header
39361      */
39362     setColumnHeader : function(col, header){
39363         this.config[col].header = header;
39364         this.fireEvent("headerchange", this, col, header);
39365     },
39366
39367     /**
39368      * Returns the tooltip for the specified column.
39369      * @param {Number} col The column index
39370      * @return {String}
39371      */
39372     getColumnTooltip : function(col){
39373             return this.config[col].tooltip;
39374     },
39375     /**
39376      * Sets the tooltip for a column.
39377      * @param {Number} col The column index
39378      * @param {String} tooltip The new tooltip
39379      */
39380     setColumnTooltip : function(col, tooltip){
39381             this.config[col].tooltip = tooltip;
39382     },
39383
39384     /**
39385      * Returns the dataIndex for the specified column.
39386      * @param {Number} col The column index
39387      * @return {Number}
39388      */
39389     getDataIndex : function(col){
39390         return this.config[col].dataIndex;
39391     },
39392
39393     /**
39394      * Sets the dataIndex for a column.
39395      * @param {Number} col The column index
39396      * @param {Number} dataIndex The new dataIndex
39397      */
39398     setDataIndex : function(col, dataIndex){
39399         this.config[col].dataIndex = dataIndex;
39400     },
39401
39402     
39403     
39404     /**
39405      * Returns true if the cell is editable.
39406      * @param {Number} colIndex The column index
39407      * @param {Number} rowIndex The row index - this is nto actually used..?
39408      * @return {Boolean}
39409      */
39410     isCellEditable : function(colIndex, rowIndex){
39411         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
39412     },
39413
39414     /**
39415      * Returns the editor defined for the cell/column.
39416      * return false or null to disable editing.
39417      * @param {Number} colIndex The column index
39418      * @param {Number} rowIndex The row index
39419      * @return {Object}
39420      */
39421     getCellEditor : function(colIndex, rowIndex){
39422         return this.config[colIndex].editor;
39423     },
39424
39425     /**
39426      * Sets if a column is editable.
39427      * @param {Number} col The column index
39428      * @param {Boolean} editable True if the column is editable
39429      */
39430     setEditable : function(col, editable){
39431         this.config[col].editable = editable;
39432     },
39433
39434
39435     /**
39436      * Returns true if the column is hidden.
39437      * @param {Number} colIndex The column index
39438      * @return {Boolean}
39439      */
39440     isHidden : function(colIndex){
39441         return this.config[colIndex].hidden;
39442     },
39443
39444
39445     /**
39446      * Returns true if the column width cannot be changed
39447      */
39448     isFixed : function(colIndex){
39449         return this.config[colIndex].fixed;
39450     },
39451
39452     /**
39453      * Returns true if the column can be resized
39454      * @return {Boolean}
39455      */
39456     isResizable : function(colIndex){
39457         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
39458     },
39459     /**
39460      * Sets if a column is hidden.
39461      * @param {Number} colIndex The column index
39462      * @param {Boolean} hidden True if the column is hidden
39463      */
39464     setHidden : function(colIndex, hidden){
39465         this.config[colIndex].hidden = hidden;
39466         this.totalWidth = null;
39467         this.fireEvent("hiddenchange", this, colIndex, hidden);
39468     },
39469
39470     /**
39471      * Sets the editor for a column.
39472      * @param {Number} col The column index
39473      * @param {Object} editor The editor object
39474      */
39475     setEditor : function(col, editor){
39476         this.config[col].editor = editor;
39477     }
39478 });
39479
39480 Roo.grid.ColumnModel.defaultRenderer = function(value){
39481         if(typeof value == "string" && value.length < 1){
39482             return "&#160;";
39483         }
39484         return value;
39485 };
39486
39487 // Alias for backwards compatibility
39488 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
39489 /*
39490  * Based on:
39491  * Ext JS Library 1.1.1
39492  * Copyright(c) 2006-2007, Ext JS, LLC.
39493  *
39494  * Originally Released Under LGPL - original licence link has changed is not relivant.
39495  *
39496  * Fork - LGPL
39497  * <script type="text/javascript">
39498  */
39499
39500 /**
39501  * @class Roo.grid.AbstractSelectionModel
39502  * @extends Roo.util.Observable
39503  * Abstract base class for grid SelectionModels.  It provides the interface that should be
39504  * implemented by descendant classes.  This class should not be directly instantiated.
39505  * @constructor
39506  */
39507 Roo.grid.AbstractSelectionModel = function(){
39508     this.locked = false;
39509     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
39510 };
39511
39512 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
39513     /** @ignore Called by the grid automatically. Do not call directly. */
39514     init : function(grid){
39515         this.grid = grid;
39516         this.initEvents();
39517     },
39518
39519     /**
39520      * Locks the selections.
39521      */
39522     lock : function(){
39523         this.locked = true;
39524     },
39525
39526     /**
39527      * Unlocks the selections.
39528      */
39529     unlock : function(){
39530         this.locked = false;
39531     },
39532
39533     /**
39534      * Returns true if the selections are locked.
39535      * @return {Boolean}
39536      */
39537     isLocked : function(){
39538         return this.locked;
39539     }
39540 });/*
39541  * Based on:
39542  * Ext JS Library 1.1.1
39543  * Copyright(c) 2006-2007, Ext JS, LLC.
39544  *
39545  * Originally Released Under LGPL - original licence link has changed is not relivant.
39546  *
39547  * Fork - LGPL
39548  * <script type="text/javascript">
39549  */
39550 /**
39551  * @extends Roo.grid.AbstractSelectionModel
39552  * @class Roo.grid.RowSelectionModel
39553  * The default SelectionModel used by {@link Roo.grid.Grid}.
39554  * It supports multiple selections and keyboard selection/navigation. 
39555  * @constructor
39556  * @param {Object} config
39557  */
39558 Roo.grid.RowSelectionModel = function(config){
39559     Roo.apply(this, config);
39560     this.selections = new Roo.util.MixedCollection(false, function(o){
39561         return o.id;
39562     });
39563
39564     this.last = false;
39565     this.lastActive = false;
39566
39567     this.addEvents({
39568         /**
39569              * @event selectionchange
39570              * Fires when the selection changes
39571              * @param {SelectionModel} this
39572              */
39573             "selectionchange" : true,
39574         /**
39575              * @event afterselectionchange
39576              * Fires after the selection changes (eg. by key press or clicking)
39577              * @param {SelectionModel} this
39578              */
39579             "afterselectionchange" : true,
39580         /**
39581              * @event beforerowselect
39582              * Fires when a row is selected being selected, return false to cancel.
39583              * @param {SelectionModel} this
39584              * @param {Number} rowIndex The selected index
39585              * @param {Boolean} keepExisting False if other selections will be cleared
39586              */
39587             "beforerowselect" : true,
39588         /**
39589              * @event rowselect
39590              * Fires when a row is selected.
39591              * @param {SelectionModel} this
39592              * @param {Number} rowIndex The selected index
39593              * @param {Roo.data.Record} r The record
39594              */
39595             "rowselect" : true,
39596         /**
39597              * @event rowdeselect
39598              * Fires when a row is deselected.
39599              * @param {SelectionModel} this
39600              * @param {Number} rowIndex The selected index
39601              */
39602         "rowdeselect" : true
39603     });
39604     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
39605     this.locked = false;
39606 };
39607
39608 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
39609     /**
39610      * @cfg {Boolean} singleSelect
39611      * True to allow selection of only one row at a time (defaults to false)
39612      */
39613     singleSelect : false,
39614
39615     // private
39616     initEvents : function(){
39617
39618         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
39619             this.grid.on("mousedown", this.handleMouseDown, this);
39620         }else{ // allow click to work like normal
39621             this.grid.on("rowclick", this.handleDragableRowClick, this);
39622         }
39623
39624         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
39625             "up" : function(e){
39626                 if(!e.shiftKey){
39627                     this.selectPrevious(e.shiftKey);
39628                 }else if(this.last !== false && this.lastActive !== false){
39629                     var last = this.last;
39630                     this.selectRange(this.last,  this.lastActive-1);
39631                     this.grid.getView().focusRow(this.lastActive);
39632                     if(last !== false){
39633                         this.last = last;
39634                     }
39635                 }else{
39636                     this.selectFirstRow();
39637                 }
39638                 this.fireEvent("afterselectionchange", this);
39639             },
39640             "down" : function(e){
39641                 if(!e.shiftKey){
39642                     this.selectNext(e.shiftKey);
39643                 }else if(this.last !== false && this.lastActive !== false){
39644                     var last = this.last;
39645                     this.selectRange(this.last,  this.lastActive+1);
39646                     this.grid.getView().focusRow(this.lastActive);
39647                     if(last !== false){
39648                         this.last = last;
39649                     }
39650                 }else{
39651                     this.selectFirstRow();
39652                 }
39653                 this.fireEvent("afterselectionchange", this);
39654             },
39655             scope: this
39656         });
39657
39658         var view = this.grid.view;
39659         view.on("refresh", this.onRefresh, this);
39660         view.on("rowupdated", this.onRowUpdated, this);
39661         view.on("rowremoved", this.onRemove, this);
39662     },
39663
39664     // private
39665     onRefresh : function(){
39666         var ds = this.grid.dataSource, i, v = this.grid.view;
39667         var s = this.selections;
39668         s.each(function(r){
39669             if((i = ds.indexOfId(r.id)) != -1){
39670                 v.onRowSelect(i);
39671                 s.add(ds.getAt(i)); // updating the selection relate data
39672             }else{
39673                 s.remove(r);
39674             }
39675         });
39676     },
39677
39678     // private
39679     onRemove : function(v, index, r){
39680         this.selections.remove(r);
39681     },
39682
39683     // private
39684     onRowUpdated : function(v, index, r){
39685         if(this.isSelected(r)){
39686             v.onRowSelect(index);
39687         }
39688     },
39689
39690     /**
39691      * Select records.
39692      * @param {Array} records The records to select
39693      * @param {Boolean} keepExisting (optional) True to keep existing selections
39694      */
39695     selectRecords : function(records, keepExisting){
39696         if(!keepExisting){
39697             this.clearSelections();
39698         }
39699         var ds = this.grid.dataSource;
39700         for(var i = 0, len = records.length; i < len; i++){
39701             this.selectRow(ds.indexOf(records[i]), true);
39702         }
39703     },
39704
39705     /**
39706      * Gets the number of selected rows.
39707      * @return {Number}
39708      */
39709     getCount : function(){
39710         return this.selections.length;
39711     },
39712
39713     /**
39714      * Selects the first row in the grid.
39715      */
39716     selectFirstRow : function(){
39717         this.selectRow(0);
39718     },
39719
39720     /**
39721      * Select the last row.
39722      * @param {Boolean} keepExisting (optional) True to keep existing selections
39723      */
39724     selectLastRow : function(keepExisting){
39725         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
39726     },
39727
39728     /**
39729      * Selects the row immediately following the last selected row.
39730      * @param {Boolean} keepExisting (optional) True to keep existing selections
39731      */
39732     selectNext : function(keepExisting){
39733         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
39734             this.selectRow(this.last+1, keepExisting);
39735             this.grid.getView().focusRow(this.last);
39736         }
39737     },
39738
39739     /**
39740      * Selects the row that precedes the last selected row.
39741      * @param {Boolean} keepExisting (optional) True to keep existing selections
39742      */
39743     selectPrevious : function(keepExisting){
39744         if(this.last){
39745             this.selectRow(this.last-1, keepExisting);
39746             this.grid.getView().focusRow(this.last);
39747         }
39748     },
39749
39750     /**
39751      * Returns the selected records
39752      * @return {Array} Array of selected records
39753      */
39754     getSelections : function(){
39755         return [].concat(this.selections.items);
39756     },
39757
39758     /**
39759      * Returns the first selected record.
39760      * @return {Record}
39761      */
39762     getSelected : function(){
39763         return this.selections.itemAt(0);
39764     },
39765
39766
39767     /**
39768      * Clears all selections.
39769      */
39770     clearSelections : function(fast){
39771         if(this.locked) {
39772             return;
39773         }
39774         if(fast !== true){
39775             var ds = this.grid.dataSource;
39776             var s = this.selections;
39777             s.each(function(r){
39778                 this.deselectRow(ds.indexOfId(r.id));
39779             }, this);
39780             s.clear();
39781         }else{
39782             this.selections.clear();
39783         }
39784         this.last = false;
39785     },
39786
39787
39788     /**
39789      * Selects all rows.
39790      */
39791     selectAll : function(){
39792         if(this.locked) {
39793             return;
39794         }
39795         this.selections.clear();
39796         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
39797             this.selectRow(i, true);
39798         }
39799     },
39800
39801     /**
39802      * Returns True if there is a selection.
39803      * @return {Boolean}
39804      */
39805     hasSelection : function(){
39806         return this.selections.length > 0;
39807     },
39808
39809     /**
39810      * Returns True if the specified row is selected.
39811      * @param {Number/Record} record The record or index of the record to check
39812      * @return {Boolean}
39813      */
39814     isSelected : function(index){
39815         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
39816         return (r && this.selections.key(r.id) ? true : false);
39817     },
39818
39819     /**
39820      * Returns True if the specified record id is selected.
39821      * @param {String} id The id of record to check
39822      * @return {Boolean}
39823      */
39824     isIdSelected : function(id){
39825         return (this.selections.key(id) ? true : false);
39826     },
39827
39828     // private
39829     handleMouseDown : function(e, t){
39830         var view = this.grid.getView(), rowIndex;
39831         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
39832             return;
39833         };
39834         if(e.shiftKey && this.last !== false){
39835             var last = this.last;
39836             this.selectRange(last, rowIndex, e.ctrlKey);
39837             this.last = last; // reset the last
39838             view.focusRow(rowIndex);
39839         }else{
39840             var isSelected = this.isSelected(rowIndex);
39841             if(e.button !== 0 && isSelected){
39842                 view.focusRow(rowIndex);
39843             }else if(e.ctrlKey && isSelected){
39844                 this.deselectRow(rowIndex);
39845             }else if(!isSelected){
39846                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
39847                 view.focusRow(rowIndex);
39848             }
39849         }
39850         this.fireEvent("afterselectionchange", this);
39851     },
39852     // private
39853     handleDragableRowClick :  function(grid, rowIndex, e) 
39854     {
39855         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
39856             this.selectRow(rowIndex, false);
39857             grid.view.focusRow(rowIndex);
39858              this.fireEvent("afterselectionchange", this);
39859         }
39860     },
39861     
39862     /**
39863      * Selects multiple rows.
39864      * @param {Array} rows Array of the indexes of the row to select
39865      * @param {Boolean} keepExisting (optional) True to keep existing selections
39866      */
39867     selectRows : function(rows, keepExisting){
39868         if(!keepExisting){
39869             this.clearSelections();
39870         }
39871         for(var i = 0, len = rows.length; i < len; i++){
39872             this.selectRow(rows[i], true);
39873         }
39874     },
39875
39876     /**
39877      * Selects a range of rows. All rows in between startRow and endRow are also selected.
39878      * @param {Number} startRow The index of the first row in the range
39879      * @param {Number} endRow The index of the last row in the range
39880      * @param {Boolean} keepExisting (optional) True to retain existing selections
39881      */
39882     selectRange : function(startRow, endRow, keepExisting){
39883         if(this.locked) {
39884             return;
39885         }
39886         if(!keepExisting){
39887             this.clearSelections();
39888         }
39889         if(startRow <= endRow){
39890             for(var i = startRow; i <= endRow; i++){
39891                 this.selectRow(i, true);
39892             }
39893         }else{
39894             for(var i = startRow; i >= endRow; i--){
39895                 this.selectRow(i, true);
39896             }
39897         }
39898     },
39899
39900     /**
39901      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
39902      * @param {Number} startRow The index of the first row in the range
39903      * @param {Number} endRow The index of the last row in the range
39904      */
39905     deselectRange : function(startRow, endRow, preventViewNotify){
39906         if(this.locked) {
39907             return;
39908         }
39909         for(var i = startRow; i <= endRow; i++){
39910             this.deselectRow(i, preventViewNotify);
39911         }
39912     },
39913
39914     /**
39915      * Selects a row.
39916      * @param {Number} row The index of the row to select
39917      * @param {Boolean} keepExisting (optional) True to keep existing selections
39918      */
39919     selectRow : function(index, keepExisting, preventViewNotify){
39920         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
39921             return;
39922         }
39923         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
39924             if(!keepExisting || this.singleSelect){
39925                 this.clearSelections();
39926             }
39927             var r = this.grid.dataSource.getAt(index);
39928             this.selections.add(r);
39929             this.last = this.lastActive = index;
39930             if(!preventViewNotify){
39931                 this.grid.getView().onRowSelect(index);
39932             }
39933             this.fireEvent("rowselect", this, index, r);
39934             this.fireEvent("selectionchange", this);
39935         }
39936     },
39937
39938     /**
39939      * Deselects a row.
39940      * @param {Number} row The index of the row to deselect
39941      */
39942     deselectRow : function(index, preventViewNotify){
39943         if(this.locked) {
39944             return;
39945         }
39946         if(this.last == index){
39947             this.last = false;
39948         }
39949         if(this.lastActive == index){
39950             this.lastActive = false;
39951         }
39952         var r = this.grid.dataSource.getAt(index);
39953         this.selections.remove(r);
39954         if(!preventViewNotify){
39955             this.grid.getView().onRowDeselect(index);
39956         }
39957         this.fireEvent("rowdeselect", this, index);
39958         this.fireEvent("selectionchange", this);
39959     },
39960
39961     // private
39962     restoreLast : function(){
39963         if(this._last){
39964             this.last = this._last;
39965         }
39966     },
39967
39968     // private
39969     acceptsNav : function(row, col, cm){
39970         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39971     },
39972
39973     // private
39974     onEditorKey : function(field, e){
39975         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
39976         if(k == e.TAB){
39977             e.stopEvent();
39978             ed.completeEdit();
39979             if(e.shiftKey){
39980                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39981             }else{
39982                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39983             }
39984         }else if(k == e.ENTER && !e.ctrlKey){
39985             e.stopEvent();
39986             ed.completeEdit();
39987             if(e.shiftKey){
39988                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
39989             }else{
39990                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
39991             }
39992         }else if(k == e.ESC){
39993             ed.cancelEdit();
39994         }
39995         if(newCell){
39996             g.startEditing(newCell[0], newCell[1]);
39997         }
39998     }
39999 });/*
40000  * Based on:
40001  * Ext JS Library 1.1.1
40002  * Copyright(c) 2006-2007, Ext JS, LLC.
40003  *
40004  * Originally Released Under LGPL - original licence link has changed is not relivant.
40005  *
40006  * Fork - LGPL
40007  * <script type="text/javascript">
40008  */
40009 /**
40010  * @class Roo.grid.CellSelectionModel
40011  * @extends Roo.grid.AbstractSelectionModel
40012  * This class provides the basic implementation for cell selection in a grid.
40013  * @constructor
40014  * @param {Object} config The object containing the configuration of this model.
40015  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
40016  */
40017 Roo.grid.CellSelectionModel = function(config){
40018     Roo.apply(this, config);
40019
40020     this.selection = null;
40021
40022     this.addEvents({
40023         /**
40024              * @event beforerowselect
40025              * Fires before a cell is selected.
40026              * @param {SelectionModel} this
40027              * @param {Number} rowIndex The selected row index
40028              * @param {Number} colIndex The selected cell index
40029              */
40030             "beforecellselect" : true,
40031         /**
40032              * @event cellselect
40033              * Fires when a cell is selected.
40034              * @param {SelectionModel} this
40035              * @param {Number} rowIndex The selected row index
40036              * @param {Number} colIndex The selected cell index
40037              */
40038             "cellselect" : true,
40039         /**
40040              * @event selectionchange
40041              * Fires when the active selection changes.
40042              * @param {SelectionModel} this
40043              * @param {Object} selection null for no selection or an object (o) with two properties
40044                 <ul>
40045                 <li>o.record: the record object for the row the selection is in</li>
40046                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
40047                 </ul>
40048              */
40049             "selectionchange" : true,
40050         /**
40051              * @event tabend
40052              * Fires when the tab (or enter) was pressed on the last editable cell
40053              * You can use this to trigger add new row.
40054              * @param {SelectionModel} this
40055              */
40056             "tabend" : true,
40057          /**
40058              * @event beforeeditnext
40059              * Fires before the next editable sell is made active
40060              * You can use this to skip to another cell or fire the tabend
40061              *    if you set cell to false
40062              * @param {Object} eventdata object : { cell : [ row, col ] } 
40063              */
40064             "beforeeditnext" : true
40065     });
40066     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
40067 };
40068
40069 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
40070     
40071     enter_is_tab: false,
40072
40073     /** @ignore */
40074     initEvents : function(){
40075         this.grid.on("mousedown", this.handleMouseDown, this);
40076         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
40077         var view = this.grid.view;
40078         view.on("refresh", this.onViewChange, this);
40079         view.on("rowupdated", this.onRowUpdated, this);
40080         view.on("beforerowremoved", this.clearSelections, this);
40081         view.on("beforerowsinserted", this.clearSelections, this);
40082         if(this.grid.isEditor){
40083             this.grid.on("beforeedit", this.beforeEdit,  this);
40084         }
40085     },
40086
40087         //private
40088     beforeEdit : function(e){
40089         this.select(e.row, e.column, false, true, e.record);
40090     },
40091
40092         //private
40093     onRowUpdated : function(v, index, r){
40094         if(this.selection && this.selection.record == r){
40095             v.onCellSelect(index, this.selection.cell[1]);
40096         }
40097     },
40098
40099         //private
40100     onViewChange : function(){
40101         this.clearSelections(true);
40102     },
40103
40104         /**
40105          * Returns the currently selected cell,.
40106          * @return {Array} The selected cell (row, column) or null if none selected.
40107          */
40108     getSelectedCell : function(){
40109         return this.selection ? this.selection.cell : null;
40110     },
40111
40112     /**
40113      * Clears all selections.
40114      * @param {Boolean} true to prevent the gridview from being notified about the change.
40115      */
40116     clearSelections : function(preventNotify){
40117         var s = this.selection;
40118         if(s){
40119             if(preventNotify !== true){
40120                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
40121             }
40122             this.selection = null;
40123             this.fireEvent("selectionchange", this, null);
40124         }
40125     },
40126
40127     /**
40128      * Returns true if there is a selection.
40129      * @return {Boolean}
40130      */
40131     hasSelection : function(){
40132         return this.selection ? true : false;
40133     },
40134
40135     /** @ignore */
40136     handleMouseDown : function(e, t){
40137         var v = this.grid.getView();
40138         if(this.isLocked()){
40139             return;
40140         };
40141         var row = v.findRowIndex(t);
40142         var cell = v.findCellIndex(t);
40143         if(row !== false && cell !== false){
40144             this.select(row, cell);
40145         }
40146     },
40147
40148     /**
40149      * Selects a cell.
40150      * @param {Number} rowIndex
40151      * @param {Number} collIndex
40152      */
40153     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
40154         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
40155             this.clearSelections();
40156             r = r || this.grid.dataSource.getAt(rowIndex);
40157             this.selection = {
40158                 record : r,
40159                 cell : [rowIndex, colIndex]
40160             };
40161             if(!preventViewNotify){
40162                 var v = this.grid.getView();
40163                 v.onCellSelect(rowIndex, colIndex);
40164                 if(preventFocus !== true){
40165                     v.focusCell(rowIndex, colIndex);
40166                 }
40167             }
40168             this.fireEvent("cellselect", this, rowIndex, colIndex);
40169             this.fireEvent("selectionchange", this, this.selection);
40170         }
40171     },
40172
40173         //private
40174     isSelectable : function(rowIndex, colIndex, cm){
40175         return !cm.isHidden(colIndex);
40176     },
40177
40178     /** @ignore */
40179     handleKeyDown : function(e){
40180         //Roo.log('Cell Sel Model handleKeyDown');
40181         if(!e.isNavKeyPress()){
40182             return;
40183         }
40184         var g = this.grid, s = this.selection;
40185         if(!s){
40186             e.stopEvent();
40187             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
40188             if(cell){
40189                 this.select(cell[0], cell[1]);
40190             }
40191             return;
40192         }
40193         var sm = this;
40194         var walk = function(row, col, step){
40195             return g.walkCells(row, col, step, sm.isSelectable,  sm);
40196         };
40197         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
40198         var newCell;
40199
40200       
40201
40202         switch(k){
40203             case e.TAB:
40204                 // handled by onEditorKey
40205                 if (g.isEditor && g.editing) {
40206                     return;
40207                 }
40208                 if(e.shiftKey) {
40209                     newCell = walk(r, c-1, -1);
40210                 } else {
40211                     newCell = walk(r, c+1, 1);
40212                 }
40213                 break;
40214             
40215             case e.DOWN:
40216                newCell = walk(r+1, c, 1);
40217                 break;
40218             
40219             case e.UP:
40220                 newCell = walk(r-1, c, -1);
40221                 break;
40222             
40223             case e.RIGHT:
40224                 newCell = walk(r, c+1, 1);
40225                 break;
40226             
40227             case e.LEFT:
40228                 newCell = walk(r, c-1, -1);
40229                 break;
40230             
40231             case e.ENTER:
40232                 
40233                 if(g.isEditor && !g.editing){
40234                    g.startEditing(r, c);
40235                    e.stopEvent();
40236                    return;
40237                 }
40238                 
40239                 
40240              break;
40241         };
40242         if(newCell){
40243             this.select(newCell[0], newCell[1]);
40244             e.stopEvent();
40245             
40246         }
40247     },
40248
40249     acceptsNav : function(row, col, cm){
40250         return !cm.isHidden(col) && cm.isCellEditable(col, row);
40251     },
40252     /**
40253      * Selects a cell.
40254      * @param {Number} field (not used) - as it's normally used as a listener
40255      * @param {Number} e - event - fake it by using
40256      *
40257      * var e = Roo.EventObjectImpl.prototype;
40258      * e.keyCode = e.TAB
40259      *
40260      * 
40261      */
40262     onEditorKey : function(field, e){
40263         
40264         var k = e.getKey(),
40265             newCell,
40266             g = this.grid,
40267             ed = g.activeEditor,
40268             forward = false;
40269         ///Roo.log('onEditorKey' + k);
40270         
40271         
40272         if (this.enter_is_tab && k == e.ENTER) {
40273             k = e.TAB;
40274         }
40275         
40276         if(k == e.TAB){
40277             if(e.shiftKey){
40278                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
40279             }else{
40280                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
40281                 forward = true;
40282             }
40283             
40284             e.stopEvent();
40285             
40286         } else if(k == e.ENTER &&  !e.ctrlKey){
40287             ed.completeEdit();
40288             e.stopEvent();
40289             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
40290         
40291                 } else if(k == e.ESC){
40292             ed.cancelEdit();
40293         }
40294                 
40295         if (newCell) {
40296             var ecall = { cell : newCell, forward : forward };
40297             this.fireEvent('beforeeditnext', ecall );
40298             newCell = ecall.cell;
40299                         forward = ecall.forward;
40300         }
40301                 
40302         if(newCell){
40303             //Roo.log('next cell after edit');
40304             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
40305         } else if (forward) {
40306             // tabbed past last
40307             this.fireEvent.defer(100, this, ['tabend',this]);
40308         }
40309     }
40310 });/*
40311  * Based on:
40312  * Ext JS Library 1.1.1
40313  * Copyright(c) 2006-2007, Ext JS, LLC.
40314  *
40315  * Originally Released Under LGPL - original licence link has changed is not relivant.
40316  *
40317  * Fork - LGPL
40318  * <script type="text/javascript">
40319  */
40320  
40321 /**
40322  * @class Roo.grid.EditorGrid
40323  * @extends Roo.grid.Grid
40324  * Class for creating and editable grid.
40325  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
40326  * The container MUST have some type of size defined for the grid to fill. The container will be 
40327  * automatically set to position relative if it isn't already.
40328  * @param {Object} dataSource The data model to bind to
40329  * @param {Object} colModel The column model with info about this grid's columns
40330  */
40331 Roo.grid.EditorGrid = function(container, config){
40332     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
40333     this.getGridEl().addClass("xedit-grid");
40334
40335     if(!this.selModel){
40336         this.selModel = new Roo.grid.CellSelectionModel();
40337     }
40338
40339     this.activeEditor = null;
40340
40341         this.addEvents({
40342             /**
40343              * @event beforeedit
40344              * Fires before cell editing is triggered. The edit event object has the following properties <br />
40345              * <ul style="padding:5px;padding-left:16px;">
40346              * <li>grid - This grid</li>
40347              * <li>record - The record being edited</li>
40348              * <li>field - The field name being edited</li>
40349              * <li>value - The value for the field being edited.</li>
40350              * <li>row - The grid row index</li>
40351              * <li>column - The grid column index</li>
40352              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
40353              * </ul>
40354              * @param {Object} e An edit event (see above for description)
40355              */
40356             "beforeedit" : true,
40357             /**
40358              * @event afteredit
40359              * Fires after a cell is edited. <br />
40360              * <ul style="padding:5px;padding-left:16px;">
40361              * <li>grid - This grid</li>
40362              * <li>record - The record being edited</li>
40363              * <li>field - The field name being edited</li>
40364              * <li>value - The value being set</li>
40365              * <li>originalValue - The original value for the field, before the edit.</li>
40366              * <li>row - The grid row index</li>
40367              * <li>column - The grid column index</li>
40368              * </ul>
40369              * @param {Object} e An edit event (see above for description)
40370              */
40371             "afteredit" : true,
40372             /**
40373              * @event validateedit
40374              * Fires after a cell is edited, but before the value is set in the record. 
40375          * You can use this to modify the value being set in the field, Return false
40376              * to cancel the change. The edit event object has the following properties <br />
40377              * <ul style="padding:5px;padding-left:16px;">
40378          * <li>editor - This editor</li>
40379              * <li>grid - This grid</li>
40380              * <li>record - The record being edited</li>
40381              * <li>field - The field name being edited</li>
40382              * <li>value - The value being set</li>
40383              * <li>originalValue - The original value for the field, before the edit.</li>
40384              * <li>row - The grid row index</li>
40385              * <li>column - The grid column index</li>
40386              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
40387              * </ul>
40388              * @param {Object} e An edit event (see above for description)
40389              */
40390             "validateedit" : true
40391         });
40392     this.on("bodyscroll", this.stopEditing,  this);
40393     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
40394 };
40395
40396 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
40397     /**
40398      * @cfg {Number} clicksToEdit
40399      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
40400      */
40401     clicksToEdit: 2,
40402
40403     // private
40404     isEditor : true,
40405     // private
40406     trackMouseOver: false, // causes very odd FF errors
40407
40408     onCellDblClick : function(g, row, col){
40409         this.startEditing(row, col);
40410     },
40411
40412     onEditComplete : function(ed, value, startValue){
40413         this.editing = false;
40414         this.activeEditor = null;
40415         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
40416         var r = ed.record;
40417         var field = this.colModel.getDataIndex(ed.col);
40418         var e = {
40419             grid: this,
40420             record: r,
40421             field: field,
40422             originalValue: startValue,
40423             value: value,
40424             row: ed.row,
40425             column: ed.col,
40426             cancel:false,
40427             editor: ed
40428         };
40429         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
40430         cell.show();
40431           
40432         if(String(value) !== String(startValue)){
40433             
40434             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
40435                 r.set(field, e.value);
40436                 // if we are dealing with a combo box..
40437                 // then we also set the 'name' colum to be the displayField
40438                 if (ed.field.displayField && ed.field.name) {
40439                     r.set(ed.field.name, ed.field.el.dom.value);
40440                 }
40441                 
40442                 delete e.cancel; //?? why!!!
40443                 this.fireEvent("afteredit", e);
40444             }
40445         } else {
40446             this.fireEvent("afteredit", e); // always fire it!
40447         }
40448         this.view.focusCell(ed.row, ed.col);
40449     },
40450
40451     /**
40452      * Starts editing the specified for the specified row/column
40453      * @param {Number} rowIndex
40454      * @param {Number} colIndex
40455      */
40456     startEditing : function(row, col){
40457         this.stopEditing();
40458         if(this.colModel.isCellEditable(col, row)){
40459             this.view.ensureVisible(row, col, true);
40460           
40461             var r = this.dataSource.getAt(row);
40462             var field = this.colModel.getDataIndex(col);
40463             var cell = Roo.get(this.view.getCell(row,col));
40464             var e = {
40465                 grid: this,
40466                 record: r,
40467                 field: field,
40468                 value: r.data[field],
40469                 row: row,
40470                 column: col,
40471                 cancel:false 
40472             };
40473             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
40474                 this.editing = true;
40475                 var ed = this.colModel.getCellEditor(col, row);
40476                 
40477                 if (!ed) {
40478                     return;
40479                 }
40480                 if(!ed.rendered){
40481                     ed.render(ed.parentEl || document.body);
40482                 }
40483                 ed.field.reset();
40484                
40485                 cell.hide();
40486                 
40487                 (function(){ // complex but required for focus issues in safari, ie and opera
40488                     ed.row = row;
40489                     ed.col = col;
40490                     ed.record = r;
40491                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
40492                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
40493                     this.activeEditor = ed;
40494                     var v = r.data[field];
40495                     ed.startEdit(this.view.getCell(row, col), v);
40496                     // combo's with 'displayField and name set
40497                     if (ed.field.displayField && ed.field.name) {
40498                         ed.field.el.dom.value = r.data[ed.field.name];
40499                     }
40500                     
40501                     
40502                 }).defer(50, this);
40503             }
40504         }
40505     },
40506         
40507     /**
40508      * Stops any active editing
40509      */
40510     stopEditing : function(){
40511         if(this.activeEditor){
40512             this.activeEditor.completeEdit();
40513         }
40514         this.activeEditor = null;
40515     },
40516         
40517          /**
40518      * Called to get grid's drag proxy text, by default returns this.ddText.
40519      * @return {String}
40520      */
40521     getDragDropText : function(){
40522         var count = this.selModel.getSelectedCell() ? 1 : 0;
40523         return String.format(this.ddText, count, count == 1 ? '' : 's');
40524     }
40525         
40526 });/*
40527  * Based on:
40528  * Ext JS Library 1.1.1
40529  * Copyright(c) 2006-2007, Ext JS, LLC.
40530  *
40531  * Originally Released Under LGPL - original licence link has changed is not relivant.
40532  *
40533  * Fork - LGPL
40534  * <script type="text/javascript">
40535  */
40536
40537 // private - not really -- you end up using it !
40538 // This is a support class used internally by the Grid components
40539
40540 /**
40541  * @class Roo.grid.GridEditor
40542  * @extends Roo.Editor
40543  * Class for creating and editable grid elements.
40544  * @param {Object} config any settings (must include field)
40545  */
40546 Roo.grid.GridEditor = function(field, config){
40547     if (!config && field.field) {
40548         config = field;
40549         field = Roo.factory(config.field, Roo.form);
40550     }
40551     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
40552     field.monitorTab = false;
40553 };
40554
40555 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
40556     
40557     /**
40558      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
40559      */
40560     
40561     alignment: "tl-tl",
40562     autoSize: "width",
40563     hideEl : false,
40564     cls: "x-small-editor x-grid-editor",
40565     shim:false,
40566     shadow:"frame"
40567 });/*
40568  * Based on:
40569  * Ext JS Library 1.1.1
40570  * Copyright(c) 2006-2007, Ext JS, LLC.
40571  *
40572  * Originally Released Under LGPL - original licence link has changed is not relivant.
40573  *
40574  * Fork - LGPL
40575  * <script type="text/javascript">
40576  */
40577   
40578
40579   
40580 Roo.grid.PropertyRecord = Roo.data.Record.create([
40581     {name:'name',type:'string'},  'value'
40582 ]);
40583
40584
40585 Roo.grid.PropertyStore = function(grid, source){
40586     this.grid = grid;
40587     this.store = new Roo.data.Store({
40588         recordType : Roo.grid.PropertyRecord
40589     });
40590     this.store.on('update', this.onUpdate,  this);
40591     if(source){
40592         this.setSource(source);
40593     }
40594     Roo.grid.PropertyStore.superclass.constructor.call(this);
40595 };
40596
40597
40598
40599 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
40600     setSource : function(o){
40601         this.source = o;
40602         this.store.removeAll();
40603         var data = [];
40604         for(var k in o){
40605             if(this.isEditableValue(o[k])){
40606                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
40607             }
40608         }
40609         this.store.loadRecords({records: data}, {}, true);
40610     },
40611
40612     onUpdate : function(ds, record, type){
40613         if(type == Roo.data.Record.EDIT){
40614             var v = record.data['value'];
40615             var oldValue = record.modified['value'];
40616             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
40617                 this.source[record.id] = v;
40618                 record.commit();
40619                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
40620             }else{
40621                 record.reject();
40622             }
40623         }
40624     },
40625
40626     getProperty : function(row){
40627        return this.store.getAt(row);
40628     },
40629
40630     isEditableValue: function(val){
40631         if(val && val instanceof Date){
40632             return true;
40633         }else if(typeof val == 'object' || typeof val == 'function'){
40634             return false;
40635         }
40636         return true;
40637     },
40638
40639     setValue : function(prop, value){
40640         this.source[prop] = value;
40641         this.store.getById(prop).set('value', value);
40642     },
40643
40644     getSource : function(){
40645         return this.source;
40646     }
40647 });
40648
40649 Roo.grid.PropertyColumnModel = function(grid, store){
40650     this.grid = grid;
40651     var g = Roo.grid;
40652     g.PropertyColumnModel.superclass.constructor.call(this, [
40653         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
40654         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
40655     ]);
40656     this.store = store;
40657     this.bselect = Roo.DomHelper.append(document.body, {
40658         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
40659             {tag: 'option', value: 'true', html: 'true'},
40660             {tag: 'option', value: 'false', html: 'false'}
40661         ]
40662     });
40663     Roo.id(this.bselect);
40664     var f = Roo.form;
40665     this.editors = {
40666         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
40667         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
40668         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
40669         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
40670         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
40671     };
40672     this.renderCellDelegate = this.renderCell.createDelegate(this);
40673     this.renderPropDelegate = this.renderProp.createDelegate(this);
40674 };
40675
40676 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
40677     
40678     
40679     nameText : 'Name',
40680     valueText : 'Value',
40681     
40682     dateFormat : 'm/j/Y',
40683     
40684     
40685     renderDate : function(dateVal){
40686         return dateVal.dateFormat(this.dateFormat);
40687     },
40688
40689     renderBool : function(bVal){
40690         return bVal ? 'true' : 'false';
40691     },
40692
40693     isCellEditable : function(colIndex, rowIndex){
40694         return colIndex == 1;
40695     },
40696
40697     getRenderer : function(col){
40698         return col == 1 ?
40699             this.renderCellDelegate : this.renderPropDelegate;
40700     },
40701
40702     renderProp : function(v){
40703         return this.getPropertyName(v);
40704     },
40705
40706     renderCell : function(val){
40707         var rv = val;
40708         if(val instanceof Date){
40709             rv = this.renderDate(val);
40710         }else if(typeof val == 'boolean'){
40711             rv = this.renderBool(val);
40712         }
40713         return Roo.util.Format.htmlEncode(rv);
40714     },
40715
40716     getPropertyName : function(name){
40717         var pn = this.grid.propertyNames;
40718         return pn && pn[name] ? pn[name] : name;
40719     },
40720
40721     getCellEditor : function(colIndex, rowIndex){
40722         var p = this.store.getProperty(rowIndex);
40723         var n = p.data['name'], val = p.data['value'];
40724         
40725         if(typeof(this.grid.customEditors[n]) == 'string'){
40726             return this.editors[this.grid.customEditors[n]];
40727         }
40728         if(typeof(this.grid.customEditors[n]) != 'undefined'){
40729             return this.grid.customEditors[n];
40730         }
40731         if(val instanceof Date){
40732             return this.editors['date'];
40733         }else if(typeof val == 'number'){
40734             return this.editors['number'];
40735         }else if(typeof val == 'boolean'){
40736             return this.editors['boolean'];
40737         }else{
40738             return this.editors['string'];
40739         }
40740     }
40741 });
40742
40743 /**
40744  * @class Roo.grid.PropertyGrid
40745  * @extends Roo.grid.EditorGrid
40746  * This class represents the  interface of a component based property grid control.
40747  * <br><br>Usage:<pre><code>
40748  var grid = new Roo.grid.PropertyGrid("my-container-id", {
40749       
40750  });
40751  // set any options
40752  grid.render();
40753  * </code></pre>
40754   
40755  * @constructor
40756  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40757  * The container MUST have some type of size defined for the grid to fill. The container will be
40758  * automatically set to position relative if it isn't already.
40759  * @param {Object} config A config object that sets properties on this grid.
40760  */
40761 Roo.grid.PropertyGrid = function(container, config){
40762     config = config || {};
40763     var store = new Roo.grid.PropertyStore(this);
40764     this.store = store;
40765     var cm = new Roo.grid.PropertyColumnModel(this, store);
40766     store.store.sort('name', 'ASC');
40767     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
40768         ds: store.store,
40769         cm: cm,
40770         enableColLock:false,
40771         enableColumnMove:false,
40772         stripeRows:false,
40773         trackMouseOver: false,
40774         clicksToEdit:1
40775     }, config));
40776     this.getGridEl().addClass('x-props-grid');
40777     this.lastEditRow = null;
40778     this.on('columnresize', this.onColumnResize, this);
40779     this.addEvents({
40780          /**
40781              * @event beforepropertychange
40782              * Fires before a property changes (return false to stop?)
40783              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40784              * @param {String} id Record Id
40785              * @param {String} newval New Value
40786          * @param {String} oldval Old Value
40787              */
40788         "beforepropertychange": true,
40789         /**
40790              * @event propertychange
40791              * Fires after a property changes
40792              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40793              * @param {String} id Record Id
40794              * @param {String} newval New Value
40795          * @param {String} oldval Old Value
40796              */
40797         "propertychange": true
40798     });
40799     this.customEditors = this.customEditors || {};
40800 };
40801 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
40802     
40803      /**
40804      * @cfg {Object} customEditors map of colnames=> custom editors.
40805      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
40806      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
40807      * false disables editing of the field.
40808          */
40809     
40810       /**
40811      * @cfg {Object} propertyNames map of property Names to their displayed value
40812          */
40813     
40814     render : function(){
40815         Roo.grid.PropertyGrid.superclass.render.call(this);
40816         this.autoSize.defer(100, this);
40817     },
40818
40819     autoSize : function(){
40820         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
40821         if(this.view){
40822             this.view.fitColumns();
40823         }
40824     },
40825
40826     onColumnResize : function(){
40827         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
40828         this.autoSize();
40829     },
40830     /**
40831      * Sets the data for the Grid
40832      * accepts a Key => Value object of all the elements avaiable.
40833      * @param {Object} data  to appear in grid.
40834      */
40835     setSource : function(source){
40836         this.store.setSource(source);
40837         //this.autoSize();
40838     },
40839     /**
40840      * Gets all the data from the grid.
40841      * @return {Object} data  data stored in grid
40842      */
40843     getSource : function(){
40844         return this.store.getSource();
40845     }
40846 });/*
40847   
40848  * Licence LGPL
40849  
40850  */
40851  
40852 /**
40853  * @class Roo.grid.Calendar
40854  * @extends Roo.util.Grid
40855  * This class extends the Grid to provide a calendar widget
40856  * <br><br>Usage:<pre><code>
40857  var grid = new Roo.grid.Calendar("my-container-id", {
40858      ds: myDataStore,
40859      cm: myColModel,
40860      selModel: mySelectionModel,
40861      autoSizeColumns: true,
40862      monitorWindowResize: false,
40863      trackMouseOver: true
40864      eventstore : real data store..
40865  });
40866  // set any options
40867  grid.render();
40868   
40869   * @constructor
40870  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40871  * The container MUST have some type of size defined for the grid to fill. The container will be
40872  * automatically set to position relative if it isn't already.
40873  * @param {Object} config A config object that sets properties on this grid.
40874  */
40875 Roo.grid.Calendar = function(container, config){
40876         // initialize the container
40877         this.container = Roo.get(container);
40878         this.container.update("");
40879         this.container.setStyle("overflow", "hidden");
40880     this.container.addClass('x-grid-container');
40881
40882     this.id = this.container.id;
40883
40884     Roo.apply(this, config);
40885     // check and correct shorthanded configs
40886     
40887     var rows = [];
40888     var d =1;
40889     for (var r = 0;r < 6;r++) {
40890         
40891         rows[r]=[];
40892         for (var c =0;c < 7;c++) {
40893             rows[r][c]= '';
40894         }
40895     }
40896     if (this.eventStore) {
40897         this.eventStore= Roo.factory(this.eventStore, Roo.data);
40898         this.eventStore.on('load',this.onLoad, this);
40899         this.eventStore.on('beforeload',this.clearEvents, this);
40900          
40901     }
40902     
40903     this.dataSource = new Roo.data.Store({
40904             proxy: new Roo.data.MemoryProxy(rows),
40905             reader: new Roo.data.ArrayReader({}, [
40906                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
40907     });
40908
40909     this.dataSource.load();
40910     this.ds = this.dataSource;
40911     this.ds.xmodule = this.xmodule || false;
40912     
40913     
40914     var cellRender = function(v,x,r)
40915     {
40916         return String.format(
40917             '<div class="fc-day  fc-widget-content"><div>' +
40918                 '<div class="fc-event-container"></div>' +
40919                 '<div class="fc-day-number">{0}</div>'+
40920                 
40921                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
40922             '</div></div>', v);
40923     
40924     }
40925     
40926     
40927     this.colModel = new Roo.grid.ColumnModel( [
40928         {
40929             xtype: 'ColumnModel',
40930             xns: Roo.grid,
40931             dataIndex : 'weekday0',
40932             header : 'Sunday',
40933             renderer : cellRender
40934         },
40935         {
40936             xtype: 'ColumnModel',
40937             xns: Roo.grid,
40938             dataIndex : 'weekday1',
40939             header : 'Monday',
40940             renderer : cellRender
40941         },
40942         {
40943             xtype: 'ColumnModel',
40944             xns: Roo.grid,
40945             dataIndex : 'weekday2',
40946             header : 'Tuesday',
40947             renderer : cellRender
40948         },
40949         {
40950             xtype: 'ColumnModel',
40951             xns: Roo.grid,
40952             dataIndex : 'weekday3',
40953             header : 'Wednesday',
40954             renderer : cellRender
40955         },
40956         {
40957             xtype: 'ColumnModel',
40958             xns: Roo.grid,
40959             dataIndex : 'weekday4',
40960             header : 'Thursday',
40961             renderer : cellRender
40962         },
40963         {
40964             xtype: 'ColumnModel',
40965             xns: Roo.grid,
40966             dataIndex : 'weekday5',
40967             header : 'Friday',
40968             renderer : cellRender
40969         },
40970         {
40971             xtype: 'ColumnModel',
40972             xns: Roo.grid,
40973             dataIndex : 'weekday6',
40974             header : 'Saturday',
40975             renderer : cellRender
40976         }
40977     ]);
40978     this.cm = this.colModel;
40979     this.cm.xmodule = this.xmodule || false;
40980  
40981         
40982           
40983     //this.selModel = new Roo.grid.CellSelectionModel();
40984     //this.sm = this.selModel;
40985     //this.selModel.init(this);
40986     
40987     
40988     if(this.width){
40989         this.container.setWidth(this.width);
40990     }
40991
40992     if(this.height){
40993         this.container.setHeight(this.height);
40994     }
40995     /** @private */
40996         this.addEvents({
40997         // raw events
40998         /**
40999          * @event click
41000          * The raw click event for the entire grid.
41001          * @param {Roo.EventObject} e
41002          */
41003         "click" : true,
41004         /**
41005          * @event dblclick
41006          * The raw dblclick event for the entire grid.
41007          * @param {Roo.EventObject} e
41008          */
41009         "dblclick" : true,
41010         /**
41011          * @event contextmenu
41012          * The raw contextmenu event for the entire grid.
41013          * @param {Roo.EventObject} e
41014          */
41015         "contextmenu" : true,
41016         /**
41017          * @event mousedown
41018          * The raw mousedown event for the entire grid.
41019          * @param {Roo.EventObject} e
41020          */
41021         "mousedown" : true,
41022         /**
41023          * @event mouseup
41024          * The raw mouseup event for the entire grid.
41025          * @param {Roo.EventObject} e
41026          */
41027         "mouseup" : true,
41028         /**
41029          * @event mouseover
41030          * The raw mouseover event for the entire grid.
41031          * @param {Roo.EventObject} e
41032          */
41033         "mouseover" : true,
41034         /**
41035          * @event mouseout
41036          * The raw mouseout event for the entire grid.
41037          * @param {Roo.EventObject} e
41038          */
41039         "mouseout" : true,
41040         /**
41041          * @event keypress
41042          * The raw keypress event for the entire grid.
41043          * @param {Roo.EventObject} e
41044          */
41045         "keypress" : true,
41046         /**
41047          * @event keydown
41048          * The raw keydown event for the entire grid.
41049          * @param {Roo.EventObject} e
41050          */
41051         "keydown" : true,
41052
41053         // custom events
41054
41055         /**
41056          * @event cellclick
41057          * Fires when a cell is clicked
41058          * @param {Grid} this
41059          * @param {Number} rowIndex
41060          * @param {Number} columnIndex
41061          * @param {Roo.EventObject} e
41062          */
41063         "cellclick" : true,
41064         /**
41065          * @event celldblclick
41066          * Fires when a cell is double clicked
41067          * @param {Grid} this
41068          * @param {Number} rowIndex
41069          * @param {Number} columnIndex
41070          * @param {Roo.EventObject} e
41071          */
41072         "celldblclick" : true,
41073         /**
41074          * @event rowclick
41075          * Fires when a row is clicked
41076          * @param {Grid} this
41077          * @param {Number} rowIndex
41078          * @param {Roo.EventObject} e
41079          */
41080         "rowclick" : true,
41081         /**
41082          * @event rowdblclick
41083          * Fires when a row is double clicked
41084          * @param {Grid} this
41085          * @param {Number} rowIndex
41086          * @param {Roo.EventObject} e
41087          */
41088         "rowdblclick" : true,
41089         /**
41090          * @event headerclick
41091          * Fires when a header is clicked
41092          * @param {Grid} this
41093          * @param {Number} columnIndex
41094          * @param {Roo.EventObject} e
41095          */
41096         "headerclick" : true,
41097         /**
41098          * @event headerdblclick
41099          * Fires when a header cell is double clicked
41100          * @param {Grid} this
41101          * @param {Number} columnIndex
41102          * @param {Roo.EventObject} e
41103          */
41104         "headerdblclick" : true,
41105         /**
41106          * @event rowcontextmenu
41107          * Fires when a row is right clicked
41108          * @param {Grid} this
41109          * @param {Number} rowIndex
41110          * @param {Roo.EventObject} e
41111          */
41112         "rowcontextmenu" : true,
41113         /**
41114          * @event cellcontextmenu
41115          * Fires when a cell is right clicked
41116          * @param {Grid} this
41117          * @param {Number} rowIndex
41118          * @param {Number} cellIndex
41119          * @param {Roo.EventObject} e
41120          */
41121          "cellcontextmenu" : true,
41122         /**
41123          * @event headercontextmenu
41124          * Fires when a header is right clicked
41125          * @param {Grid} this
41126          * @param {Number} columnIndex
41127          * @param {Roo.EventObject} e
41128          */
41129         "headercontextmenu" : true,
41130         /**
41131          * @event bodyscroll
41132          * Fires when the body element is scrolled
41133          * @param {Number} scrollLeft
41134          * @param {Number} scrollTop
41135          */
41136         "bodyscroll" : true,
41137         /**
41138          * @event columnresize
41139          * Fires when the user resizes a column
41140          * @param {Number} columnIndex
41141          * @param {Number} newSize
41142          */
41143         "columnresize" : true,
41144         /**
41145          * @event columnmove
41146          * Fires when the user moves a column
41147          * @param {Number} oldIndex
41148          * @param {Number} newIndex
41149          */
41150         "columnmove" : true,
41151         /**
41152          * @event startdrag
41153          * Fires when row(s) start being dragged
41154          * @param {Grid} this
41155          * @param {Roo.GridDD} dd The drag drop object
41156          * @param {event} e The raw browser event
41157          */
41158         "startdrag" : true,
41159         /**
41160          * @event enddrag
41161          * Fires when a drag operation is complete
41162          * @param {Grid} this
41163          * @param {Roo.GridDD} dd The drag drop object
41164          * @param {event} e The raw browser event
41165          */
41166         "enddrag" : true,
41167         /**
41168          * @event dragdrop
41169          * Fires when dragged row(s) are dropped on a valid DD target
41170          * @param {Grid} this
41171          * @param {Roo.GridDD} dd The drag drop object
41172          * @param {String} targetId The target drag drop object
41173          * @param {event} e The raw browser event
41174          */
41175         "dragdrop" : true,
41176         /**
41177          * @event dragover
41178          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
41179          * @param {Grid} this
41180          * @param {Roo.GridDD} dd The drag drop object
41181          * @param {String} targetId The target drag drop object
41182          * @param {event} e The raw browser event
41183          */
41184         "dragover" : true,
41185         /**
41186          * @event dragenter
41187          *  Fires when the dragged row(s) first cross another DD target while being dragged
41188          * @param {Grid} this
41189          * @param {Roo.GridDD} dd The drag drop object
41190          * @param {String} targetId The target drag drop object
41191          * @param {event} e The raw browser event
41192          */
41193         "dragenter" : true,
41194         /**
41195          * @event dragout
41196          * Fires when the dragged row(s) leave another DD target while being dragged
41197          * @param {Grid} this
41198          * @param {Roo.GridDD} dd The drag drop object
41199          * @param {String} targetId The target drag drop object
41200          * @param {event} e The raw browser event
41201          */
41202         "dragout" : true,
41203         /**
41204          * @event rowclass
41205          * Fires when a row is rendered, so you can change add a style to it.
41206          * @param {GridView} gridview   The grid view
41207          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
41208          */
41209         'rowclass' : true,
41210
41211         /**
41212          * @event render
41213          * Fires when the grid is rendered
41214          * @param {Grid} grid
41215          */
41216         'render' : true,
41217             /**
41218              * @event select
41219              * Fires when a date is selected
41220              * @param {DatePicker} this
41221              * @param {Date} date The selected date
41222              */
41223         'select': true,
41224         /**
41225              * @event monthchange
41226              * Fires when the displayed month changes 
41227              * @param {DatePicker} this
41228              * @param {Date} date The selected month
41229              */
41230         'monthchange': true,
41231         /**
41232              * @event evententer
41233              * Fires when mouse over an event
41234              * @param {Calendar} this
41235              * @param {event} Event
41236              */
41237         'evententer': true,
41238         /**
41239              * @event eventleave
41240              * Fires when the mouse leaves an
41241              * @param {Calendar} this
41242              * @param {event}
41243              */
41244         'eventleave': true,
41245         /**
41246              * @event eventclick
41247              * Fires when the mouse click an
41248              * @param {Calendar} this
41249              * @param {event}
41250              */
41251         'eventclick': true,
41252         /**
41253              * @event eventrender
41254              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
41255              * @param {Calendar} this
41256              * @param {data} data to be modified
41257              */
41258         'eventrender': true
41259         
41260     });
41261
41262     Roo.grid.Grid.superclass.constructor.call(this);
41263     this.on('render', function() {
41264         this.view.el.addClass('x-grid-cal'); 
41265         
41266         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
41267
41268     },this);
41269     
41270     if (!Roo.grid.Calendar.style) {
41271         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
41272             
41273             
41274             '.x-grid-cal .x-grid-col' :  {
41275                 height: 'auto !important',
41276                 'vertical-align': 'top'
41277             },
41278             '.x-grid-cal  .fc-event-hori' : {
41279                 height: '14px'
41280             }
41281              
41282             
41283         }, Roo.id());
41284     }
41285
41286     
41287     
41288 };
41289 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
41290     /**
41291      * @cfg {Store} eventStore The store that loads events.
41292      */
41293     eventStore : 25,
41294
41295      
41296     activeDate : false,
41297     startDay : 0,
41298     autoWidth : true,
41299     monitorWindowResize : false,
41300
41301     
41302     resizeColumns : function() {
41303         var col = (this.view.el.getWidth() / 7) - 3;
41304         // loop through cols, and setWidth
41305         for(var i =0 ; i < 7 ; i++){
41306             this.cm.setColumnWidth(i, col);
41307         }
41308     },
41309      setDate :function(date) {
41310         
41311         Roo.log('setDate?');
41312         
41313         this.resizeColumns();
41314         var vd = this.activeDate;
41315         this.activeDate = date;
41316 //        if(vd && this.el){
41317 //            var t = date.getTime();
41318 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
41319 //                Roo.log('using add remove');
41320 //                
41321 //                this.fireEvent('monthchange', this, date);
41322 //                
41323 //                this.cells.removeClass("fc-state-highlight");
41324 //                this.cells.each(function(c){
41325 //                   if(c.dateValue == t){
41326 //                       c.addClass("fc-state-highlight");
41327 //                       setTimeout(function(){
41328 //                            try{c.dom.firstChild.focus();}catch(e){}
41329 //                       }, 50);
41330 //                       return false;
41331 //                   }
41332 //                   return true;
41333 //                });
41334 //                return;
41335 //            }
41336 //        }
41337         
41338         var days = date.getDaysInMonth();
41339         
41340         var firstOfMonth = date.getFirstDateOfMonth();
41341         var startingPos = firstOfMonth.getDay()-this.startDay;
41342         
41343         if(startingPos < this.startDay){
41344             startingPos += 7;
41345         }
41346         
41347         var pm = date.add(Date.MONTH, -1);
41348         var prevStart = pm.getDaysInMonth()-startingPos;
41349 //        
41350         
41351         
41352         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
41353         
41354         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
41355         //this.cells.addClassOnOver('fc-state-hover');
41356         
41357         var cells = this.cells.elements;
41358         var textEls = this.textNodes;
41359         
41360         //Roo.each(cells, function(cell){
41361         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
41362         //});
41363         
41364         days += startingPos;
41365
41366         // convert everything to numbers so it's fast
41367         var day = 86400000;
41368         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
41369         //Roo.log(d);
41370         //Roo.log(pm);
41371         //Roo.log(prevStart);
41372         
41373         var today = new Date().clearTime().getTime();
41374         var sel = date.clearTime().getTime();
41375         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
41376         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
41377         var ddMatch = this.disabledDatesRE;
41378         var ddText = this.disabledDatesText;
41379         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
41380         var ddaysText = this.disabledDaysText;
41381         var format = this.format;
41382         
41383         var setCellClass = function(cal, cell){
41384             
41385             //Roo.log('set Cell Class');
41386             cell.title = "";
41387             var t = d.getTime();
41388             
41389             //Roo.log(d);
41390             
41391             
41392             cell.dateValue = t;
41393             if(t == today){
41394                 cell.className += " fc-today";
41395                 cell.className += " fc-state-highlight";
41396                 cell.title = cal.todayText;
41397             }
41398             if(t == sel){
41399                 // disable highlight in other month..
41400                 cell.className += " fc-state-highlight";
41401                 
41402             }
41403             // disabling
41404             if(t < min) {
41405                 //cell.className = " fc-state-disabled";
41406                 cell.title = cal.minText;
41407                 return;
41408             }
41409             if(t > max) {
41410                 //cell.className = " fc-state-disabled";
41411                 cell.title = cal.maxText;
41412                 return;
41413             }
41414             if(ddays){
41415                 if(ddays.indexOf(d.getDay()) != -1){
41416                     // cell.title = ddaysText;
41417                    // cell.className = " fc-state-disabled";
41418                 }
41419             }
41420             if(ddMatch && format){
41421                 var fvalue = d.dateFormat(format);
41422                 if(ddMatch.test(fvalue)){
41423                     cell.title = ddText.replace("%0", fvalue);
41424                    cell.className = " fc-state-disabled";
41425                 }
41426             }
41427             
41428             if (!cell.initialClassName) {
41429                 cell.initialClassName = cell.dom.className;
41430             }
41431             
41432             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
41433         };
41434
41435         var i = 0;
41436         
41437         for(; i < startingPos; i++) {
41438             cells[i].dayName =  (++prevStart);
41439             Roo.log(textEls[i]);
41440             d.setDate(d.getDate()+1);
41441             
41442             //cells[i].className = "fc-past fc-other-month";
41443             setCellClass(this, cells[i]);
41444         }
41445         
41446         var intDay = 0;
41447         
41448         for(; i < days; i++){
41449             intDay = i - startingPos + 1;
41450             cells[i].dayName =  (intDay);
41451             d.setDate(d.getDate()+1);
41452             
41453             cells[i].className = ''; // "x-date-active";
41454             setCellClass(this, cells[i]);
41455         }
41456         var extraDays = 0;
41457         
41458         for(; i < 42; i++) {
41459             //textEls[i].innerHTML = (++extraDays);
41460             
41461             d.setDate(d.getDate()+1);
41462             cells[i].dayName = (++extraDays);
41463             cells[i].className = "fc-future fc-other-month";
41464             setCellClass(this, cells[i]);
41465         }
41466         
41467         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
41468         
41469         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
41470         
41471         // this will cause all the cells to mis
41472         var rows= [];
41473         var i =0;
41474         for (var r = 0;r < 6;r++) {
41475             for (var c =0;c < 7;c++) {
41476                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
41477             }    
41478         }
41479         
41480         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
41481         for(i=0;i<cells.length;i++) {
41482             
41483             this.cells.elements[i].dayName = cells[i].dayName ;
41484             this.cells.elements[i].className = cells[i].className;
41485             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
41486             this.cells.elements[i].title = cells[i].title ;
41487             this.cells.elements[i].dateValue = cells[i].dateValue ;
41488         }
41489         
41490         
41491         
41492         
41493         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
41494         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
41495         
41496         ////if(totalRows != 6){
41497             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
41498            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
41499        // }
41500         
41501         this.fireEvent('monthchange', this, date);
41502         
41503         
41504     },
41505  /**
41506      * Returns the grid's SelectionModel.
41507      * @return {SelectionModel}
41508      */
41509     getSelectionModel : function(){
41510         if(!this.selModel){
41511             this.selModel = new Roo.grid.CellSelectionModel();
41512         }
41513         return this.selModel;
41514     },
41515
41516     load: function() {
41517         this.eventStore.load()
41518         
41519         
41520         
41521     },
41522     
41523     findCell : function(dt) {
41524         dt = dt.clearTime().getTime();
41525         var ret = false;
41526         this.cells.each(function(c){
41527             //Roo.log("check " +c.dateValue + '?=' + dt);
41528             if(c.dateValue == dt){
41529                 ret = c;
41530                 return false;
41531             }
41532             return true;
41533         });
41534         
41535         return ret;
41536     },
41537     
41538     findCells : function(rec) {
41539         var s = rec.data.start_dt.clone().clearTime().getTime();
41540        // Roo.log(s);
41541         var e= rec.data.end_dt.clone().clearTime().getTime();
41542        // Roo.log(e);
41543         var ret = [];
41544         this.cells.each(function(c){
41545              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
41546             
41547             if(c.dateValue > e){
41548                 return ;
41549             }
41550             if(c.dateValue < s){
41551                 return ;
41552             }
41553             ret.push(c);
41554         });
41555         
41556         return ret;    
41557     },
41558     
41559     findBestRow: function(cells)
41560     {
41561         var ret = 0;
41562         
41563         for (var i =0 ; i < cells.length;i++) {
41564             ret  = Math.max(cells[i].rows || 0,ret);
41565         }
41566         return ret;
41567         
41568     },
41569     
41570     
41571     addItem : function(rec)
41572     {
41573         // look for vertical location slot in
41574         var cells = this.findCells(rec);
41575         
41576         rec.row = this.findBestRow(cells);
41577         
41578         // work out the location.
41579         
41580         var crow = false;
41581         var rows = [];
41582         for(var i =0; i < cells.length; i++) {
41583             if (!crow) {
41584                 crow = {
41585                     start : cells[i],
41586                     end :  cells[i]
41587                 };
41588                 continue;
41589             }
41590             if (crow.start.getY() == cells[i].getY()) {
41591                 // on same row.
41592                 crow.end = cells[i];
41593                 continue;
41594             }
41595             // different row.
41596             rows.push(crow);
41597             crow = {
41598                 start: cells[i],
41599                 end : cells[i]
41600             };
41601             
41602         }
41603         
41604         rows.push(crow);
41605         rec.els = [];
41606         rec.rows = rows;
41607         rec.cells = cells;
41608         for (var i = 0; i < cells.length;i++) {
41609             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
41610             
41611         }
41612         
41613         
41614     },
41615     
41616     clearEvents: function() {
41617         
41618         if (!this.eventStore.getCount()) {
41619             return;
41620         }
41621         // reset number of rows in cells.
41622         Roo.each(this.cells.elements, function(c){
41623             c.rows = 0;
41624         });
41625         
41626         this.eventStore.each(function(e) {
41627             this.clearEvent(e);
41628         },this);
41629         
41630     },
41631     
41632     clearEvent : function(ev)
41633     {
41634         if (ev.els) {
41635             Roo.each(ev.els, function(el) {
41636                 el.un('mouseenter' ,this.onEventEnter, this);
41637                 el.un('mouseleave' ,this.onEventLeave, this);
41638                 el.remove();
41639             },this);
41640             ev.els = [];
41641         }
41642     },
41643     
41644     
41645     renderEvent : function(ev,ctr) {
41646         if (!ctr) {
41647              ctr = this.view.el.select('.fc-event-container',true).first();
41648         }
41649         
41650          
41651         this.clearEvent(ev);
41652             //code
41653        
41654         
41655         
41656         ev.els = [];
41657         var cells = ev.cells;
41658         var rows = ev.rows;
41659         this.fireEvent('eventrender', this, ev);
41660         
41661         for(var i =0; i < rows.length; i++) {
41662             
41663             cls = '';
41664             if (i == 0) {
41665                 cls += ' fc-event-start';
41666             }
41667             if ((i+1) == rows.length) {
41668                 cls += ' fc-event-end';
41669             }
41670             
41671             //Roo.log(ev.data);
41672             // how many rows should it span..
41673             var cg = this.eventTmpl.append(ctr,Roo.apply({
41674                 fccls : cls
41675                 
41676             }, ev.data) , true);
41677             
41678             
41679             cg.on('mouseenter' ,this.onEventEnter, this, ev);
41680             cg.on('mouseleave' ,this.onEventLeave, this, ev);
41681             cg.on('click', this.onEventClick, this, ev);
41682             
41683             ev.els.push(cg);
41684             
41685             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
41686             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
41687             //Roo.log(cg);
41688              
41689             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
41690             cg.setWidth(ebox.right - sbox.x -2);
41691         }
41692     },
41693     
41694     renderEvents: function()
41695     {   
41696         // first make sure there is enough space..
41697         
41698         if (!this.eventTmpl) {
41699             this.eventTmpl = new Roo.Template(
41700                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
41701                     '<div class="fc-event-inner">' +
41702                         '<span class="fc-event-time">{time}</span>' +
41703                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
41704                     '</div>' +
41705                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
41706                 '</div>'
41707             );
41708                 
41709         }
41710                
41711         
41712         
41713         this.cells.each(function(c) {
41714             //Roo.log(c.select('.fc-day-content div',true).first());
41715             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
41716         });
41717         
41718         var ctr = this.view.el.select('.fc-event-container',true).first();
41719         
41720         var cls;
41721         this.eventStore.each(function(ev){
41722             
41723             this.renderEvent(ev);
41724              
41725              
41726         }, this);
41727         this.view.layout();
41728         
41729     },
41730     
41731     onEventEnter: function (e, el,event,d) {
41732         this.fireEvent('evententer', this, el, event);
41733     },
41734     
41735     onEventLeave: function (e, el,event,d) {
41736         this.fireEvent('eventleave', this, el, event);
41737     },
41738     
41739     onEventClick: function (e, el,event,d) {
41740         this.fireEvent('eventclick', this, el, event);
41741     },
41742     
41743     onMonthChange: function () {
41744         this.store.load();
41745     },
41746     
41747     onLoad: function () {
41748         
41749         //Roo.log('calendar onload');
41750 //         
41751         if(this.eventStore.getCount() > 0){
41752             
41753            
41754             
41755             this.eventStore.each(function(d){
41756                 
41757                 
41758                 // FIXME..
41759                 var add =   d.data;
41760                 if (typeof(add.end_dt) == 'undefined')  {
41761                     Roo.log("Missing End time in calendar data: ");
41762                     Roo.log(d);
41763                     return;
41764                 }
41765                 if (typeof(add.start_dt) == 'undefined')  {
41766                     Roo.log("Missing Start time in calendar data: ");
41767                     Roo.log(d);
41768                     return;
41769                 }
41770                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
41771                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
41772                 add.id = add.id || d.id;
41773                 add.title = add.title || '??';
41774                 
41775                 this.addItem(d);
41776                 
41777              
41778             },this);
41779         }
41780         
41781         this.renderEvents();
41782     }
41783     
41784
41785 });
41786 /*
41787  grid : {
41788                 xtype: 'Grid',
41789                 xns: Roo.grid,
41790                 listeners : {
41791                     render : function ()
41792                     {
41793                         _this.grid = this;
41794                         
41795                         if (!this.view.el.hasClass('course-timesheet')) {
41796                             this.view.el.addClass('course-timesheet');
41797                         }
41798                         if (this.tsStyle) {
41799                             this.ds.load({});
41800                             return; 
41801                         }
41802                         Roo.log('width');
41803                         Roo.log(_this.grid.view.el.getWidth());
41804                         
41805                         
41806                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
41807                             '.course-timesheet .x-grid-row' : {
41808                                 height: '80px'
41809                             },
41810                             '.x-grid-row td' : {
41811                                 'vertical-align' : 0
41812                             },
41813                             '.course-edit-link' : {
41814                                 'color' : 'blue',
41815                                 'text-overflow' : 'ellipsis',
41816                                 'overflow' : 'hidden',
41817                                 'white-space' : 'nowrap',
41818                                 'cursor' : 'pointer'
41819                             },
41820                             '.sub-link' : {
41821                                 'color' : 'green'
41822                             },
41823                             '.de-act-sup-link' : {
41824                                 'color' : 'purple',
41825                                 'text-decoration' : 'line-through'
41826                             },
41827                             '.de-act-link' : {
41828                                 'color' : 'red',
41829                                 'text-decoration' : 'line-through'
41830                             },
41831                             '.course-timesheet .course-highlight' : {
41832                                 'border-top-style': 'dashed !important',
41833                                 'border-bottom-bottom': 'dashed !important'
41834                             },
41835                             '.course-timesheet .course-item' : {
41836                                 'font-family'   : 'tahoma, arial, helvetica',
41837                                 'font-size'     : '11px',
41838                                 'overflow'      : 'hidden',
41839                                 'padding-left'  : '10px',
41840                                 'padding-right' : '10px',
41841                                 'padding-top' : '10px' 
41842                             }
41843                             
41844                         }, Roo.id());
41845                                 this.ds.load({});
41846                     }
41847                 },
41848                 autoWidth : true,
41849                 monitorWindowResize : false,
41850                 cellrenderer : function(v,x,r)
41851                 {
41852                     return v;
41853                 },
41854                 sm : {
41855                     xtype: 'CellSelectionModel',
41856                     xns: Roo.grid
41857                 },
41858                 dataSource : {
41859                     xtype: 'Store',
41860                     xns: Roo.data,
41861                     listeners : {
41862                         beforeload : function (_self, options)
41863                         {
41864                             options.params = options.params || {};
41865                             options.params._month = _this.monthField.getValue();
41866                             options.params.limit = 9999;
41867                             options.params['sort'] = 'when_dt';    
41868                             options.params['dir'] = 'ASC';    
41869                             this.proxy.loadResponse = this.loadResponse;
41870                             Roo.log("load?");
41871                             //this.addColumns();
41872                         },
41873                         load : function (_self, records, options)
41874                         {
41875                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
41876                                 // if you click on the translation.. you can edit it...
41877                                 var el = Roo.get(this);
41878                                 var id = el.dom.getAttribute('data-id');
41879                                 var d = el.dom.getAttribute('data-date');
41880                                 var t = el.dom.getAttribute('data-time');
41881                                 //var id = this.child('span').dom.textContent;
41882                                 
41883                                 //Roo.log(this);
41884                                 Pman.Dialog.CourseCalendar.show({
41885                                     id : id,
41886                                     when_d : d,
41887                                     when_t : t,
41888                                     productitem_active : id ? 1 : 0
41889                                 }, function() {
41890                                     _this.grid.ds.load({});
41891                                 });
41892                            
41893                            });
41894                            
41895                            _this.panel.fireEvent('resize', [ '', '' ]);
41896                         }
41897                     },
41898                     loadResponse : function(o, success, response){
41899                             // this is overridden on before load..
41900                             
41901                             Roo.log("our code?");       
41902                             //Roo.log(success);
41903                             //Roo.log(response)
41904                             delete this.activeRequest;
41905                             if(!success){
41906                                 this.fireEvent("loadexception", this, o, response);
41907                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41908                                 return;
41909                             }
41910                             var result;
41911                             try {
41912                                 result = o.reader.read(response);
41913                             }catch(e){
41914                                 Roo.log("load exception?");
41915                                 this.fireEvent("loadexception", this, o, response, e);
41916                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41917                                 return;
41918                             }
41919                             Roo.log("ready...");        
41920                             // loop through result.records;
41921                             // and set this.tdate[date] = [] << array of records..
41922                             _this.tdata  = {};
41923                             Roo.each(result.records, function(r){
41924                                 //Roo.log(r.data);
41925                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
41926                                     _this.tdata[r.data.when_dt.format('j')] = [];
41927                                 }
41928                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
41929                             });
41930                             
41931                             //Roo.log(_this.tdata);
41932                             
41933                             result.records = [];
41934                             result.totalRecords = 6;
41935                     
41936                             // let's generate some duumy records for the rows.
41937                             //var st = _this.dateField.getValue();
41938                             
41939                             // work out monday..
41940                             //st = st.add(Date.DAY, -1 * st.format('w'));
41941                             
41942                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41943                             
41944                             var firstOfMonth = date.getFirstDayOfMonth();
41945                             var days = date.getDaysInMonth();
41946                             var d = 1;
41947                             var firstAdded = false;
41948                             for (var i = 0; i < result.totalRecords ; i++) {
41949                                 //var d= st.add(Date.DAY, i);
41950                                 var row = {};
41951                                 var added = 0;
41952                                 for(var w = 0 ; w < 7 ; w++){
41953                                     if(!firstAdded && firstOfMonth != w){
41954                                         continue;
41955                                     }
41956                                     if(d > days){
41957                                         continue;
41958                                     }
41959                                     firstAdded = true;
41960                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
41961                                     row['weekday'+w] = String.format(
41962                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
41963                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
41964                                                     d,
41965                                                     date.format('Y-m-')+dd
41966                                                 );
41967                                     added++;
41968                                     if(typeof(_this.tdata[d]) != 'undefined'){
41969                                         Roo.each(_this.tdata[d], function(r){
41970                                             var is_sub = '';
41971                                             var deactive = '';
41972                                             var id = r.id;
41973                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
41974                                             if(r.parent_id*1>0){
41975                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
41976                                                 id = r.parent_id;
41977                                             }
41978                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
41979                                                 deactive = 'de-act-link';
41980                                             }
41981                                             
41982                                             row['weekday'+w] += String.format(
41983                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
41984                                                     id, //0
41985                                                     r.product_id_name, //1
41986                                                     r.when_dt.format('h:ia'), //2
41987                                                     is_sub, //3
41988                                                     deactive, //4
41989                                                     desc // 5
41990                                             );
41991                                         });
41992                                     }
41993                                     d++;
41994                                 }
41995                                 
41996                                 // only do this if something added..
41997                                 if(added > 0){ 
41998                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
41999                                 }
42000                                 
42001                                 
42002                                 // push it twice. (second one with an hour..
42003                                 
42004                             }
42005                             //Roo.log(result);
42006                             this.fireEvent("load", this, o, o.request.arg);
42007                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
42008                         },
42009                     sortInfo : {field: 'when_dt', direction : 'ASC' },
42010                     proxy : {
42011                         xtype: 'HttpProxy',
42012                         xns: Roo.data,
42013                         method : 'GET',
42014                         url : baseURL + '/Roo/Shop_course.php'
42015                     },
42016                     reader : {
42017                         xtype: 'JsonReader',
42018                         xns: Roo.data,
42019                         id : 'id',
42020                         fields : [
42021                             {
42022                                 'name': 'id',
42023                                 'type': 'int'
42024                             },
42025                             {
42026                                 'name': 'when_dt',
42027                                 'type': 'string'
42028                             },
42029                             {
42030                                 'name': 'end_dt',
42031                                 'type': 'string'
42032                             },
42033                             {
42034                                 'name': 'parent_id',
42035                                 'type': 'int'
42036                             },
42037                             {
42038                                 'name': 'product_id',
42039                                 'type': 'int'
42040                             },
42041                             {
42042                                 'name': 'productitem_id',
42043                                 'type': 'int'
42044                             },
42045                             {
42046                                 'name': 'guid',
42047                                 'type': 'int'
42048                             }
42049                         ]
42050                     }
42051                 },
42052                 toolbar : {
42053                     xtype: 'Toolbar',
42054                     xns: Roo,
42055                     items : [
42056                         {
42057                             xtype: 'Button',
42058                             xns: Roo.Toolbar,
42059                             listeners : {
42060                                 click : function (_self, e)
42061                                 {
42062                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
42063                                     sd.setMonth(sd.getMonth()-1);
42064                                     _this.monthField.setValue(sd.format('Y-m-d'));
42065                                     _this.grid.ds.load({});
42066                                 }
42067                             },
42068                             text : "Back"
42069                         },
42070                         {
42071                             xtype: 'Separator',
42072                             xns: Roo.Toolbar
42073                         },
42074                         {
42075                             xtype: 'MonthField',
42076                             xns: Roo.form,
42077                             listeners : {
42078                                 render : function (_self)
42079                                 {
42080                                     _this.monthField = _self;
42081                                    // _this.monthField.set  today
42082                                 },
42083                                 select : function (combo, date)
42084                                 {
42085                                     _this.grid.ds.load({});
42086                                 }
42087                             },
42088                             value : (function() { return new Date(); })()
42089                         },
42090                         {
42091                             xtype: 'Separator',
42092                             xns: Roo.Toolbar
42093                         },
42094                         {
42095                             xtype: 'TextItem',
42096                             xns: Roo.Toolbar,
42097                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
42098                         },
42099                         {
42100                             xtype: 'Fill',
42101                             xns: Roo.Toolbar
42102                         },
42103                         {
42104                             xtype: 'Button',
42105                             xns: Roo.Toolbar,
42106                             listeners : {
42107                                 click : function (_self, e)
42108                                 {
42109                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
42110                                     sd.setMonth(sd.getMonth()+1);
42111                                     _this.monthField.setValue(sd.format('Y-m-d'));
42112                                     _this.grid.ds.load({});
42113                                 }
42114                             },
42115                             text : "Next"
42116                         }
42117                     ]
42118                 },
42119                  
42120             }
42121         };
42122         
42123         *//*
42124  * Based on:
42125  * Ext JS Library 1.1.1
42126  * Copyright(c) 2006-2007, Ext JS, LLC.
42127  *
42128  * Originally Released Under LGPL - original licence link has changed is not relivant.
42129  *
42130  * Fork - LGPL
42131  * <script type="text/javascript">
42132  */
42133  
42134 /**
42135  * @class Roo.LoadMask
42136  * A simple utility class for generically masking elements while loading data.  If the element being masked has
42137  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
42138  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
42139  * element's UpdateManager load indicator and will be destroyed after the initial load.
42140  * @constructor
42141  * Create a new LoadMask
42142  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
42143  * @param {Object} config The config object
42144  */
42145 Roo.LoadMask = function(el, config){
42146     this.el = Roo.get(el);
42147     Roo.apply(this, config);
42148     if(this.store){
42149         this.store.on('beforeload', this.onBeforeLoad, this);
42150         this.store.on('load', this.onLoad, this);
42151         this.store.on('loadexception', this.onLoadException, this);
42152         this.removeMask = false;
42153     }else{
42154         var um = this.el.getUpdateManager();
42155         um.showLoadIndicator = false; // disable the default indicator
42156         um.on('beforeupdate', this.onBeforeLoad, this);
42157         um.on('update', this.onLoad, this);
42158         um.on('failure', this.onLoad, this);
42159         this.removeMask = true;
42160     }
42161 };
42162
42163 Roo.LoadMask.prototype = {
42164     /**
42165      * @cfg {Boolean} removeMask
42166      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
42167      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
42168      */
42169     /**
42170      * @cfg {String} msg
42171      * The text to display in a centered loading message box (defaults to 'Loading...')
42172      */
42173     msg : 'Loading...',
42174     /**
42175      * @cfg {String} msgCls
42176      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
42177      */
42178     msgCls : 'x-mask-loading',
42179
42180     /**
42181      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
42182      * @type Boolean
42183      */
42184     disabled: false,
42185
42186     /**
42187      * Disables the mask to prevent it from being displayed
42188      */
42189     disable : function(){
42190        this.disabled = true;
42191     },
42192
42193     /**
42194      * Enables the mask so that it can be displayed
42195      */
42196     enable : function(){
42197         this.disabled = false;
42198     },
42199     
42200     onLoadException : function()
42201     {
42202         Roo.log(arguments);
42203         
42204         if (typeof(arguments[3]) != 'undefined') {
42205             Roo.MessageBox.alert("Error loading",arguments[3]);
42206         } 
42207         /*
42208         try {
42209             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
42210                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
42211             }   
42212         } catch(e) {
42213             
42214         }
42215         */
42216     
42217         
42218         
42219         this.el.unmask(this.removeMask);
42220     },
42221     // private
42222     onLoad : function()
42223     {
42224         this.el.unmask(this.removeMask);
42225     },
42226
42227     // private
42228     onBeforeLoad : function(){
42229         if(!this.disabled){
42230             this.el.mask(this.msg, this.msgCls);
42231         }
42232     },
42233
42234     // private
42235     destroy : function(){
42236         if(this.store){
42237             this.store.un('beforeload', this.onBeforeLoad, this);
42238             this.store.un('load', this.onLoad, this);
42239             this.store.un('loadexception', this.onLoadException, this);
42240         }else{
42241             var um = this.el.getUpdateManager();
42242             um.un('beforeupdate', this.onBeforeLoad, this);
42243             um.un('update', this.onLoad, this);
42244             um.un('failure', this.onLoad, this);
42245         }
42246     }
42247 };/*
42248  * Based on:
42249  * Ext JS Library 1.1.1
42250  * Copyright(c) 2006-2007, Ext JS, LLC.
42251  *
42252  * Originally Released Under LGPL - original licence link has changed is not relivant.
42253  *
42254  * Fork - LGPL
42255  * <script type="text/javascript">
42256  */
42257
42258
42259 /**
42260  * @class Roo.XTemplate
42261  * @extends Roo.Template
42262  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
42263 <pre><code>
42264 var t = new Roo.XTemplate(
42265         '&lt;select name="{name}"&gt;',
42266                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
42267         '&lt;/select&gt;'
42268 );
42269  
42270 // then append, applying the master template values
42271  </code></pre>
42272  *
42273  * Supported features:
42274  *
42275  *  Tags:
42276
42277 <pre><code>
42278       {a_variable} - output encoded.
42279       {a_variable.format:("Y-m-d")} - call a method on the variable
42280       {a_variable:raw} - unencoded output
42281       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
42282       {a_variable:this.method_on_template(...)} - call a method on the template object.
42283  
42284 </code></pre>
42285  *  The tpl tag:
42286 <pre><code>
42287         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
42288         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
42289         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
42290         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
42291   
42292         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
42293         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
42294 </code></pre>
42295  *      
42296  */
42297 Roo.XTemplate = function()
42298 {
42299     Roo.XTemplate.superclass.constructor.apply(this, arguments);
42300     if (this.html) {
42301         this.compile();
42302     }
42303 };
42304
42305
42306 Roo.extend(Roo.XTemplate, Roo.Template, {
42307
42308     /**
42309      * The various sub templates
42310      */
42311     tpls : false,
42312     /**
42313      *
42314      * basic tag replacing syntax
42315      * WORD:WORD()
42316      *
42317      * // you can fake an object call by doing this
42318      *  x.t:(test,tesT) 
42319      * 
42320      */
42321     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
42322
42323     /**
42324      * compile the template
42325      *
42326      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
42327      *
42328      */
42329     compile: function()
42330     {
42331         var s = this.html;
42332      
42333         s = ['<tpl>', s, '</tpl>'].join('');
42334     
42335         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
42336             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
42337             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
42338             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
42339             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
42340             m,
42341             id     = 0,
42342             tpls   = [];
42343     
42344         while(true == !!(m = s.match(re))){
42345             var forMatch   = m[0].match(nameRe),
42346                 ifMatch   = m[0].match(ifRe),
42347                 execMatch   = m[0].match(execRe),
42348                 namedMatch   = m[0].match(namedRe),
42349                 
42350                 exp  = null, 
42351                 fn   = null,
42352                 exec = null,
42353                 name = forMatch && forMatch[1] ? forMatch[1] : '';
42354                 
42355             if (ifMatch) {
42356                 // if - puts fn into test..
42357                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
42358                 if(exp){
42359                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
42360                 }
42361             }
42362             
42363             if (execMatch) {
42364                 // exec - calls a function... returns empty if true is  returned.
42365                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
42366                 if(exp){
42367                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
42368                 }
42369             }
42370             
42371             
42372             if (name) {
42373                 // for = 
42374                 switch(name){
42375                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
42376                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
42377                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
42378                 }
42379             }
42380             var uid = namedMatch ? namedMatch[1] : id;
42381             
42382             
42383             tpls.push({
42384                 id:     namedMatch ? namedMatch[1] : id,
42385                 target: name,
42386                 exec:   exec,
42387                 test:   fn,
42388                 body:   m[1] || ''
42389             });
42390             if (namedMatch) {
42391                 s = s.replace(m[0], '');
42392             } else { 
42393                 s = s.replace(m[0], '{xtpl'+ id + '}');
42394             }
42395             ++id;
42396         }
42397         this.tpls = [];
42398         for(var i = tpls.length-1; i >= 0; --i){
42399             this.compileTpl(tpls[i]);
42400             this.tpls[tpls[i].id] = tpls[i];
42401         }
42402         this.master = tpls[tpls.length-1];
42403         return this;
42404     },
42405     /**
42406      * same as applyTemplate, except it's done to one of the subTemplates
42407      * when using named templates, you can do:
42408      *
42409      * var str = pl.applySubTemplate('your-name', values);
42410      *
42411      * 
42412      * @param {Number} id of the template
42413      * @param {Object} values to apply to template
42414      * @param {Object} parent (normaly the instance of this object)
42415      */
42416     applySubTemplate : function(id, values, parent)
42417     {
42418         
42419         
42420         var t = this.tpls[id];
42421         
42422         
42423         try { 
42424             if(t.test && !t.test.call(this, values, parent)){
42425                 return '';
42426             }
42427         } catch(e) {
42428             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
42429             Roo.log(e.toString());
42430             Roo.log(t.test);
42431             return ''
42432         }
42433         try { 
42434             
42435             if(t.exec && t.exec.call(this, values, parent)){
42436                 return '';
42437             }
42438         } catch(e) {
42439             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
42440             Roo.log(e.toString());
42441             Roo.log(t.exec);
42442             return ''
42443         }
42444         try {
42445             var vs = t.target ? t.target.call(this, values, parent) : values;
42446             parent = t.target ? values : parent;
42447             if(t.target && vs instanceof Array){
42448                 var buf = [];
42449                 for(var i = 0, len = vs.length; i < len; i++){
42450                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
42451                 }
42452                 return buf.join('');
42453             }
42454             return t.compiled.call(this, vs, parent);
42455         } catch (e) {
42456             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
42457             Roo.log(e.toString());
42458             Roo.log(t.compiled);
42459             return '';
42460         }
42461     },
42462
42463     compileTpl : function(tpl)
42464     {
42465         var fm = Roo.util.Format;
42466         var useF = this.disableFormats !== true;
42467         var sep = Roo.isGecko ? "+" : ",";
42468         var undef = function(str) {
42469             Roo.log("Property not found :"  + str);
42470             return '';
42471         };
42472         
42473         var fn = function(m, name, format, args)
42474         {
42475             //Roo.log(arguments);
42476             args = args ? args.replace(/\\'/g,"'") : args;
42477             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
42478             if (typeof(format) == 'undefined') {
42479                 format= 'htmlEncode';
42480             }
42481             if (format == 'raw' ) {
42482                 format = false;
42483             }
42484             
42485             if(name.substr(0, 4) == 'xtpl'){
42486                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
42487             }
42488             
42489             // build an array of options to determine if value is undefined..
42490             
42491             // basically get 'xxxx.yyyy' then do
42492             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
42493             //    (function () { Roo.log("Property not found"); return ''; })() :
42494             //    ......
42495             
42496             var udef_ar = [];
42497             var lookfor = '';
42498             Roo.each(name.split('.'), function(st) {
42499                 lookfor += (lookfor.length ? '.': '') + st;
42500                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
42501             });
42502             
42503             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
42504             
42505             
42506             if(format && useF){
42507                 
42508                 args = args ? ',' + args : "";
42509                  
42510                 if(format.substr(0, 5) != "this."){
42511                     format = "fm." + format + '(';
42512                 }else{
42513                     format = 'this.call("'+ format.substr(5) + '", ';
42514                     args = ", values";
42515                 }
42516                 
42517                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
42518             }
42519              
42520             if (args.length) {
42521                 // called with xxyx.yuu:(test,test)
42522                 // change to ()
42523                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
42524             }
42525             // raw.. - :raw modifier..
42526             return "'"+ sep + udef_st  + name + ")"+sep+"'";
42527             
42528         };
42529         var body;
42530         // branched to use + in gecko and [].join() in others
42531         if(Roo.isGecko){
42532             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
42533                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
42534                     "';};};";
42535         }else{
42536             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
42537             body.push(tpl.body.replace(/(\r\n|\n)/g,
42538                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
42539             body.push("'].join('');};};");
42540             body = body.join('');
42541         }
42542         
42543         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
42544        
42545         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
42546         eval(body);
42547         
42548         return this;
42549     },
42550
42551     applyTemplate : function(values){
42552         return this.master.compiled.call(this, values, {});
42553         //var s = this.subs;
42554     },
42555
42556     apply : function(){
42557         return this.applyTemplate.apply(this, arguments);
42558     }
42559
42560  });
42561
42562 Roo.XTemplate.from = function(el){
42563     el = Roo.getDom(el);
42564     return new Roo.XTemplate(el.value || el.innerHTML);
42565 };