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                         editorcore.syncValue();
28410                     }
28411                 }
28412             }));
28413              
28414         }
28415         
28416         var _this = this;
28417         
28418         if(nm == 'BODY'){
28419             tb.addSeparator();
28420         
28421             tb.addButton( {
28422                 text: 'Stylesheets',
28423
28424                 listeners : {
28425                     click : function ()
28426                     {
28427                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
28428                     }
28429                 }
28430             });
28431         }
28432         
28433         tb.addFill();
28434         tb.addButton( {
28435             text: 'Remove Tag',
28436     
28437             listeners : {
28438                 click : function ()
28439                 {
28440                     // remove
28441                     // undo does not work.
28442                      
28443                     var sn = tb.selectedNode;
28444                     
28445                     var pn = sn.parentNode;
28446                     
28447                     var stn =  sn.childNodes[0];
28448                     var en = sn.childNodes[sn.childNodes.length - 1 ];
28449                     while (sn.childNodes.length) {
28450                         var node = sn.childNodes[0];
28451                         sn.removeChild(node);
28452                         //Roo.log(node);
28453                         pn.insertBefore(node, sn);
28454                         
28455                     }
28456                     pn.removeChild(sn);
28457                     var range = editorcore.createRange();
28458         
28459                     range.setStart(stn,0);
28460                     range.setEnd(en,0); //????
28461                     //range.selectNode(sel);
28462                     
28463                     
28464                     var selection = editorcore.getSelection();
28465                     selection.removeAllRanges();
28466                     selection.addRange(range);
28467                     
28468                     
28469                     
28470                     //_this.updateToolbar(null, null, pn);
28471                     _this.updateToolbar(null, null, null);
28472                     _this.footDisp.dom.innerHTML = ''; 
28473                 }
28474             }
28475             
28476                     
28477                 
28478             
28479         });
28480         
28481         
28482         tb.el.on('click', function(e){
28483             e.preventDefault(); // what does this do?
28484         });
28485         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
28486         tb.el.hide();
28487         tb.name = nm;
28488         // dont need to disable them... as they will get hidden
28489         return tb;
28490          
28491         
28492     },
28493     buildFooter : function()
28494     {
28495         
28496         var fel = this.editor.wrap.createChild();
28497         this.footer = new Roo.Toolbar(fel);
28498         // toolbar has scrolly on left / right?
28499         var footDisp= new Roo.Toolbar.Fill();
28500         var _t = this;
28501         this.footer.add(
28502             {
28503                 text : '&lt;',
28504                 xtype: 'Button',
28505                 handler : function() {
28506                     _t.footDisp.scrollTo('left',0,true)
28507                 }
28508             }
28509         );
28510         this.footer.add( footDisp );
28511         this.footer.add( 
28512             {
28513                 text : '&gt;',
28514                 xtype: 'Button',
28515                 handler : function() {
28516                     // no animation..
28517                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
28518                 }
28519             }
28520         );
28521         var fel = Roo.get(footDisp.el);
28522         fel.addClass('x-editor-context');
28523         this.footDispWrap = fel; 
28524         this.footDispWrap.overflow  = 'hidden';
28525         
28526         this.footDisp = fel.createChild();
28527         this.footDispWrap.on('click', this.onContextClick, this)
28528         
28529         
28530     },
28531     onContextClick : function (ev,dom)
28532     {
28533         ev.preventDefault();
28534         var  cn = dom.className;
28535         //Roo.log(cn);
28536         if (!cn.match(/x-ed-loc-/)) {
28537             return;
28538         }
28539         var n = cn.split('-').pop();
28540         var ans = this.footerEls;
28541         var sel = ans[n];
28542         
28543          // pick
28544         var range = this.editorcore.createRange();
28545         
28546         range.selectNodeContents(sel);
28547         //range.selectNode(sel);
28548         
28549         
28550         var selection = this.editorcore.getSelection();
28551         selection.removeAllRanges();
28552         selection.addRange(range);
28553         
28554         
28555         
28556         this.updateToolbar(null, null, sel);
28557         
28558         
28559     }
28560     
28561     
28562     
28563     
28564     
28565 });
28566
28567
28568
28569
28570
28571 /*
28572  * Based on:
28573  * Ext JS Library 1.1.1
28574  * Copyright(c) 2006-2007, Ext JS, LLC.
28575  *
28576  * Originally Released Under LGPL - original licence link has changed is not relivant.
28577  *
28578  * Fork - LGPL
28579  * <script type="text/javascript">
28580  */
28581  
28582 /**
28583  * @class Roo.form.BasicForm
28584  * @extends Roo.util.Observable
28585  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
28586  * @constructor
28587  * @param {String/HTMLElement/Roo.Element} el The form element or its id
28588  * @param {Object} config Configuration options
28589  */
28590 Roo.form.BasicForm = function(el, config){
28591     this.allItems = [];
28592     this.childForms = [];
28593     Roo.apply(this, config);
28594     /*
28595      * The Roo.form.Field items in this form.
28596      * @type MixedCollection
28597      */
28598      
28599      
28600     this.items = new Roo.util.MixedCollection(false, function(o){
28601         return o.id || (o.id = Roo.id());
28602     });
28603     this.addEvents({
28604         /**
28605          * @event beforeaction
28606          * Fires before any action is performed. Return false to cancel the action.
28607          * @param {Form} this
28608          * @param {Action} action The action to be performed
28609          */
28610         beforeaction: true,
28611         /**
28612          * @event actionfailed
28613          * Fires when an action fails.
28614          * @param {Form} this
28615          * @param {Action} action The action that failed
28616          */
28617         actionfailed : true,
28618         /**
28619          * @event actioncomplete
28620          * Fires when an action is completed.
28621          * @param {Form} this
28622          * @param {Action} action The action that completed
28623          */
28624         actioncomplete : true
28625     });
28626     if(el){
28627         this.initEl(el);
28628     }
28629     Roo.form.BasicForm.superclass.constructor.call(this);
28630 };
28631
28632 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
28633     /**
28634      * @cfg {String} method
28635      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
28636      */
28637     /**
28638      * @cfg {DataReader} reader
28639      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
28640      * This is optional as there is built-in support for processing JSON.
28641      */
28642     /**
28643      * @cfg {DataReader} errorReader
28644      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
28645      * This is completely optional as there is built-in support for processing JSON.
28646      */
28647     /**
28648      * @cfg {String} url
28649      * The URL to use for form actions if one isn't supplied in the action options.
28650      */
28651     /**
28652      * @cfg {Boolean} fileUpload
28653      * Set to true if this form is a file upload.
28654      */
28655      
28656     /**
28657      * @cfg {Object} baseParams
28658      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
28659      */
28660      /**
28661      
28662     /**
28663      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
28664      */
28665     timeout: 30,
28666
28667     // private
28668     activeAction : null,
28669
28670     /**
28671      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
28672      * or setValues() data instead of when the form was first created.
28673      */
28674     trackResetOnLoad : false,
28675     
28676     
28677     /**
28678      * childForms - used for multi-tab forms
28679      * @type {Array}
28680      */
28681     childForms : false,
28682     
28683     /**
28684      * allItems - full list of fields.
28685      * @type {Array}
28686      */
28687     allItems : false,
28688     
28689     /**
28690      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
28691      * element by passing it or its id or mask the form itself by passing in true.
28692      * @type Mixed
28693      */
28694     waitMsgTarget : false,
28695
28696     // private
28697     initEl : function(el){
28698         this.el = Roo.get(el);
28699         this.id = this.el.id || Roo.id();
28700         this.el.on('submit', this.onSubmit, this);
28701         this.el.addClass('x-form');
28702     },
28703
28704     // private
28705     onSubmit : function(e){
28706         e.stopEvent();
28707     },
28708
28709     /**
28710      * Returns true if client-side validation on the form is successful.
28711      * @return Boolean
28712      */
28713     isValid : function(){
28714         var valid = true;
28715         this.items.each(function(f){
28716            if(!f.validate()){
28717                valid = false;
28718            }
28719         });
28720         return valid;
28721     },
28722
28723     /**
28724      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
28725      * @return Boolean
28726      */
28727     isDirty : function(){
28728         var dirty = false;
28729         this.items.each(function(f){
28730            if(f.isDirty()){
28731                dirty = true;
28732                return false;
28733            }
28734         });
28735         return dirty;
28736     },
28737     
28738     /**
28739      * Returns true if any fields in this form have changed since their original load. (New version)
28740      * @return Boolean
28741      */
28742     
28743     hasChanged : function()
28744     {
28745         var dirty = false;
28746         this.items.each(function(f){
28747            if(f.hasChanged()){
28748                dirty = true;
28749                return false;
28750            }
28751         });
28752         return dirty;
28753         
28754     },
28755     /**
28756      * Resets all hasChanged to 'false' -
28757      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
28758      * So hasChanged storage is only to be used for this purpose
28759      * @return Boolean
28760      */
28761     resetHasChanged : function()
28762     {
28763         this.items.each(function(f){
28764            f.resetHasChanged();
28765         });
28766         
28767     },
28768     
28769     
28770     /**
28771      * Performs a predefined action (submit or load) or custom actions you define on this form.
28772      * @param {String} actionName The name of the action type
28773      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
28774      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
28775      * accept other config options):
28776      * <pre>
28777 Property          Type             Description
28778 ----------------  ---------------  ----------------------------------------------------------------------------------
28779 url               String           The url for the action (defaults to the form's url)
28780 method            String           The form method to use (defaults to the form's method, or POST if not defined)
28781 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
28782 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
28783                                    validate the form on the client (defaults to false)
28784      * </pre>
28785      * @return {BasicForm} this
28786      */
28787     doAction : function(action, options){
28788         if(typeof action == 'string'){
28789             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
28790         }
28791         if(this.fireEvent('beforeaction', this, action) !== false){
28792             this.beforeAction(action);
28793             action.run.defer(100, action);
28794         }
28795         return this;
28796     },
28797
28798     /**
28799      * Shortcut to do a submit action.
28800      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28801      * @return {BasicForm} this
28802      */
28803     submit : function(options){
28804         this.doAction('submit', options);
28805         return this;
28806     },
28807
28808     /**
28809      * Shortcut to do a load action.
28810      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28811      * @return {BasicForm} this
28812      */
28813     load : function(options){
28814         this.doAction('load', options);
28815         return this;
28816     },
28817
28818     /**
28819      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
28820      * @param {Record} record The record to edit
28821      * @return {BasicForm} this
28822      */
28823     updateRecord : function(record){
28824         record.beginEdit();
28825         var fs = record.fields;
28826         fs.each(function(f){
28827             var field = this.findField(f.name);
28828             if(field){
28829                 record.set(f.name, field.getValue());
28830             }
28831         }, this);
28832         record.endEdit();
28833         return this;
28834     },
28835
28836     /**
28837      * Loads an Roo.data.Record into this form.
28838      * @param {Record} record The record to load
28839      * @return {BasicForm} this
28840      */
28841     loadRecord : function(record){
28842         this.setValues(record.data);
28843         return this;
28844     },
28845
28846     // private
28847     beforeAction : function(action){
28848         var o = action.options;
28849         
28850        
28851         if(this.waitMsgTarget === true){
28852             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
28853         }else if(this.waitMsgTarget){
28854             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
28855             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
28856         }else {
28857             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
28858         }
28859          
28860     },
28861
28862     // private
28863     afterAction : function(action, success){
28864         this.activeAction = null;
28865         var o = action.options;
28866         
28867         if(this.waitMsgTarget === true){
28868             this.el.unmask();
28869         }else if(this.waitMsgTarget){
28870             this.waitMsgTarget.unmask();
28871         }else{
28872             Roo.MessageBox.updateProgress(1);
28873             Roo.MessageBox.hide();
28874         }
28875          
28876         if(success){
28877             if(o.reset){
28878                 this.reset();
28879             }
28880             Roo.callback(o.success, o.scope, [this, action]);
28881             this.fireEvent('actioncomplete', this, action);
28882             
28883         }else{
28884             
28885             // failure condition..
28886             // we have a scenario where updates need confirming.
28887             // eg. if a locking scenario exists..
28888             // we look for { errors : { needs_confirm : true }} in the response.
28889             if (
28890                 (typeof(action.result) != 'undefined')  &&
28891                 (typeof(action.result.errors) != 'undefined')  &&
28892                 (typeof(action.result.errors.needs_confirm) != 'undefined')
28893            ){
28894                 var _t = this;
28895                 Roo.MessageBox.confirm(
28896                     "Change requires confirmation",
28897                     action.result.errorMsg,
28898                     function(r) {
28899                         if (r != 'yes') {
28900                             return;
28901                         }
28902                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
28903                     }
28904                     
28905                 );
28906                 
28907                 
28908                 
28909                 return;
28910             }
28911             
28912             Roo.callback(o.failure, o.scope, [this, action]);
28913             // show an error message if no failed handler is set..
28914             if (!this.hasListener('actionfailed')) {
28915                 Roo.MessageBox.alert("Error",
28916                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
28917                         action.result.errorMsg :
28918                         "Saving Failed, please check your entries or try again"
28919                 );
28920             }
28921             
28922             this.fireEvent('actionfailed', this, action);
28923         }
28924         
28925     },
28926
28927     /**
28928      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
28929      * @param {String} id The value to search for
28930      * @return Field
28931      */
28932     findField : function(id){
28933         var field = this.items.get(id);
28934         if(!field){
28935             this.items.each(function(f){
28936                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
28937                     field = f;
28938                     return false;
28939                 }
28940             });
28941         }
28942         return field || null;
28943     },
28944
28945     /**
28946      * Add a secondary form to this one, 
28947      * Used to provide tabbed forms. One form is primary, with hidden values 
28948      * which mirror the elements from the other forms.
28949      * 
28950      * @param {Roo.form.Form} form to add.
28951      * 
28952      */
28953     addForm : function(form)
28954     {
28955        
28956         if (this.childForms.indexOf(form) > -1) {
28957             // already added..
28958             return;
28959         }
28960         this.childForms.push(form);
28961         var n = '';
28962         Roo.each(form.allItems, function (fe) {
28963             
28964             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
28965             if (this.findField(n)) { // already added..
28966                 return;
28967             }
28968             var add = new Roo.form.Hidden({
28969                 name : n
28970             });
28971             add.render(this.el);
28972             
28973             this.add( add );
28974         }, this);
28975         
28976     },
28977     /**
28978      * Mark fields in this form invalid in bulk.
28979      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
28980      * @return {BasicForm} this
28981      */
28982     markInvalid : function(errors){
28983         if(errors instanceof Array){
28984             for(var i = 0, len = errors.length; i < len; i++){
28985                 var fieldError = errors[i];
28986                 var f = this.findField(fieldError.id);
28987                 if(f){
28988                     f.markInvalid(fieldError.msg);
28989                 }
28990             }
28991         }else{
28992             var field, id;
28993             for(id in errors){
28994                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
28995                     field.markInvalid(errors[id]);
28996                 }
28997             }
28998         }
28999         Roo.each(this.childForms || [], function (f) {
29000             f.markInvalid(errors);
29001         });
29002         
29003         return this;
29004     },
29005
29006     /**
29007      * Set values for fields in this form in bulk.
29008      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
29009      * @return {BasicForm} this
29010      */
29011     setValues : function(values){
29012         if(values instanceof Array){ // array of objects
29013             for(var i = 0, len = values.length; i < len; i++){
29014                 var v = values[i];
29015                 var f = this.findField(v.id);
29016                 if(f){
29017                     f.setValue(v.value);
29018                     if(this.trackResetOnLoad){
29019                         f.originalValue = f.getValue();
29020                     }
29021                 }
29022             }
29023         }else{ // object hash
29024             var field, id;
29025             for(id in values){
29026                 if(typeof values[id] != 'function' && (field = this.findField(id))){
29027                     
29028                     if (field.setFromData && 
29029                         field.valueField && 
29030                         field.displayField &&
29031                         // combos' with local stores can 
29032                         // be queried via setValue()
29033                         // to set their value..
29034                         (field.store && !field.store.isLocal)
29035                         ) {
29036                         // it's a combo
29037                         var sd = { };
29038                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
29039                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
29040                         field.setFromData(sd);
29041                         
29042                     } else {
29043                         field.setValue(values[id]);
29044                     }
29045                     
29046                     
29047                     if(this.trackResetOnLoad){
29048                         field.originalValue = field.getValue();
29049                     }
29050                 }
29051             }
29052         }
29053         this.resetHasChanged();
29054         
29055         
29056         Roo.each(this.childForms || [], function (f) {
29057             f.setValues(values);
29058             f.resetHasChanged();
29059         });
29060                 
29061         return this;
29062     },
29063
29064     /**
29065      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
29066      * they are returned as an array.
29067      * @param {Boolean} asString
29068      * @return {Object}
29069      */
29070     getValues : function(asString){
29071         if (this.childForms) {
29072             // copy values from the child forms
29073             Roo.each(this.childForms, function (f) {
29074                 this.setValues(f.getValues());
29075             }, this);
29076         }
29077         
29078         
29079         
29080         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
29081         if(asString === true){
29082             return fs;
29083         }
29084         return Roo.urlDecode(fs);
29085     },
29086     
29087     /**
29088      * Returns the fields in this form as an object with key/value pairs. 
29089      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
29090      * @return {Object}
29091      */
29092     getFieldValues : function(with_hidden)
29093     {
29094         if (this.childForms) {
29095             // copy values from the child forms
29096             // should this call getFieldValues - probably not as we do not currently copy
29097             // hidden fields when we generate..
29098             Roo.each(this.childForms, function (f) {
29099                 this.setValues(f.getValues());
29100             }, this);
29101         }
29102         
29103         var ret = {};
29104         this.items.each(function(f){
29105             if (!f.getName()) {
29106                 return;
29107             }
29108             var v = f.getValue();
29109             if (f.inputType =='radio') {
29110                 if (typeof(ret[f.getName()]) == 'undefined') {
29111                     ret[f.getName()] = ''; // empty..
29112                 }
29113                 
29114                 if (!f.el.dom.checked) {
29115                     return;
29116                     
29117                 }
29118                 v = f.el.dom.value;
29119                 
29120             }
29121             
29122             // not sure if this supported any more..
29123             if ((typeof(v) == 'object') && f.getRawValue) {
29124                 v = f.getRawValue() ; // dates..
29125             }
29126             // combo boxes where name != hiddenName...
29127             if (f.name != f.getName()) {
29128                 ret[f.name] = f.getRawValue();
29129             }
29130             ret[f.getName()] = v;
29131         });
29132         
29133         return ret;
29134     },
29135
29136     /**
29137      * Clears all invalid messages in this form.
29138      * @return {BasicForm} this
29139      */
29140     clearInvalid : function(){
29141         this.items.each(function(f){
29142            f.clearInvalid();
29143         });
29144         
29145         Roo.each(this.childForms || [], function (f) {
29146             f.clearInvalid();
29147         });
29148         
29149         
29150         return this;
29151     },
29152
29153     /**
29154      * Resets this form.
29155      * @return {BasicForm} this
29156      */
29157     reset : function(){
29158         this.items.each(function(f){
29159             f.reset();
29160         });
29161         
29162         Roo.each(this.childForms || [], function (f) {
29163             f.reset();
29164         });
29165         this.resetHasChanged();
29166         
29167         return this;
29168     },
29169
29170     /**
29171      * Add Roo.form components to this form.
29172      * @param {Field} field1
29173      * @param {Field} field2 (optional)
29174      * @param {Field} etc (optional)
29175      * @return {BasicForm} this
29176      */
29177     add : function(){
29178         this.items.addAll(Array.prototype.slice.call(arguments, 0));
29179         return this;
29180     },
29181
29182
29183     /**
29184      * Removes a field from the items collection (does NOT remove its markup).
29185      * @param {Field} field
29186      * @return {BasicForm} this
29187      */
29188     remove : function(field){
29189         this.items.remove(field);
29190         return this;
29191     },
29192
29193     /**
29194      * Looks at the fields in this form, checks them for an id attribute,
29195      * and calls applyTo on the existing dom element with that id.
29196      * @return {BasicForm} this
29197      */
29198     render : function(){
29199         this.items.each(function(f){
29200             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
29201                 f.applyTo(f.id);
29202             }
29203         });
29204         return this;
29205     },
29206
29207     /**
29208      * Calls {@link Ext#apply} for all fields in this form with the passed object.
29209      * @param {Object} values
29210      * @return {BasicForm} this
29211      */
29212     applyToFields : function(o){
29213         this.items.each(function(f){
29214            Roo.apply(f, o);
29215         });
29216         return this;
29217     },
29218
29219     /**
29220      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
29221      * @param {Object} values
29222      * @return {BasicForm} this
29223      */
29224     applyIfToFields : function(o){
29225         this.items.each(function(f){
29226            Roo.applyIf(f, o);
29227         });
29228         return this;
29229     }
29230 });
29231
29232 // back compat
29233 Roo.BasicForm = Roo.form.BasicForm;/*
29234  * Based on:
29235  * Ext JS Library 1.1.1
29236  * Copyright(c) 2006-2007, Ext JS, LLC.
29237  *
29238  * Originally Released Under LGPL - original licence link has changed is not relivant.
29239  *
29240  * Fork - LGPL
29241  * <script type="text/javascript">
29242  */
29243
29244 /**
29245  * @class Roo.form.Form
29246  * @extends Roo.form.BasicForm
29247  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
29248  * @constructor
29249  * @param {Object} config Configuration options
29250  */
29251 Roo.form.Form = function(config){
29252     var xitems =  [];
29253     if (config.items) {
29254         xitems = config.items;
29255         delete config.items;
29256     }
29257    
29258     
29259     Roo.form.Form.superclass.constructor.call(this, null, config);
29260     this.url = this.url || this.action;
29261     if(!this.root){
29262         this.root = new Roo.form.Layout(Roo.applyIf({
29263             id: Roo.id()
29264         }, config));
29265     }
29266     this.active = this.root;
29267     /**
29268      * Array of all the buttons that have been added to this form via {@link addButton}
29269      * @type Array
29270      */
29271     this.buttons = [];
29272     this.allItems = [];
29273     this.addEvents({
29274         /**
29275          * @event clientvalidation
29276          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
29277          * @param {Form} this
29278          * @param {Boolean} valid true if the form has passed client-side validation
29279          */
29280         clientvalidation: true,
29281         /**
29282          * @event rendered
29283          * Fires when the form is rendered
29284          * @param {Roo.form.Form} form
29285          */
29286         rendered : true
29287     });
29288     
29289     if (this.progressUrl) {
29290             // push a hidden field onto the list of fields..
29291             this.addxtype( {
29292                     xns: Roo.form, 
29293                     xtype : 'Hidden', 
29294                     name : 'UPLOAD_IDENTIFIER' 
29295             });
29296         }
29297         
29298     
29299     Roo.each(xitems, this.addxtype, this);
29300     
29301     
29302     
29303 };
29304
29305 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
29306     /**
29307      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
29308      */
29309     /**
29310      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
29311      */
29312     /**
29313      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
29314      */
29315     buttonAlign:'center',
29316
29317     /**
29318      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
29319      */
29320     minButtonWidth:75,
29321
29322     /**
29323      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
29324      * This property cascades to child containers if not set.
29325      */
29326     labelAlign:'left',
29327
29328     /**
29329      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
29330      * fires a looping event with that state. This is required to bind buttons to the valid
29331      * state using the config value formBind:true on the button.
29332      */
29333     monitorValid : false,
29334
29335     /**
29336      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
29337      */
29338     monitorPoll : 200,
29339     
29340     /**
29341      * @cfg {String} progressUrl - Url to return progress data 
29342      */
29343     
29344     progressUrl : false,
29345   
29346     /**
29347      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
29348      * fields are added and the column is closed. If no fields are passed the column remains open
29349      * until end() is called.
29350      * @param {Object} config The config to pass to the column
29351      * @param {Field} field1 (optional)
29352      * @param {Field} field2 (optional)
29353      * @param {Field} etc (optional)
29354      * @return Column The column container object
29355      */
29356     column : function(c){
29357         var col = new Roo.form.Column(c);
29358         this.start(col);
29359         if(arguments.length > 1){ // duplicate code required because of Opera
29360             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29361             this.end();
29362         }
29363         return col;
29364     },
29365
29366     /**
29367      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
29368      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
29369      * until end() is called.
29370      * @param {Object} config The config to pass to the fieldset
29371      * @param {Field} field1 (optional)
29372      * @param {Field} field2 (optional)
29373      * @param {Field} etc (optional)
29374      * @return FieldSet The fieldset container object
29375      */
29376     fieldset : function(c){
29377         var fs = new Roo.form.FieldSet(c);
29378         this.start(fs);
29379         if(arguments.length > 1){ // duplicate code required because of Opera
29380             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29381             this.end();
29382         }
29383         return fs;
29384     },
29385
29386     /**
29387      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
29388      * fields are added and the container is closed. If no fields are passed the container remains open
29389      * until end() is called.
29390      * @param {Object} config The config to pass to the Layout
29391      * @param {Field} field1 (optional)
29392      * @param {Field} field2 (optional)
29393      * @param {Field} etc (optional)
29394      * @return Layout The container object
29395      */
29396     container : function(c){
29397         var l = new Roo.form.Layout(c);
29398         this.start(l);
29399         if(arguments.length > 1){ // duplicate code required because of Opera
29400             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29401             this.end();
29402         }
29403         return l;
29404     },
29405
29406     /**
29407      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
29408      * @param {Object} container A Roo.form.Layout or subclass of Layout
29409      * @return {Form} this
29410      */
29411     start : function(c){
29412         // cascade label info
29413         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
29414         this.active.stack.push(c);
29415         c.ownerCt = this.active;
29416         this.active = c;
29417         return this;
29418     },
29419
29420     /**
29421      * Closes the current open container
29422      * @return {Form} this
29423      */
29424     end : function(){
29425         if(this.active == this.root){
29426             return this;
29427         }
29428         this.active = this.active.ownerCt;
29429         return this;
29430     },
29431
29432     /**
29433      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
29434      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
29435      * as the label of the field.
29436      * @param {Field} field1
29437      * @param {Field} field2 (optional)
29438      * @param {Field} etc. (optional)
29439      * @return {Form} this
29440      */
29441     add : function(){
29442         this.active.stack.push.apply(this.active.stack, arguments);
29443         this.allItems.push.apply(this.allItems,arguments);
29444         var r = [];
29445         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
29446             if(a[i].isFormField){
29447                 r.push(a[i]);
29448             }
29449         }
29450         if(r.length > 0){
29451             Roo.form.Form.superclass.add.apply(this, r);
29452         }
29453         return this;
29454     },
29455     
29456
29457     
29458     
29459     
29460      /**
29461      * Find any element that has been added to a form, using it's ID or name
29462      * This can include framesets, columns etc. along with regular fields..
29463      * @param {String} id - id or name to find.
29464      
29465      * @return {Element} e - or false if nothing found.
29466      */
29467     findbyId : function(id)
29468     {
29469         var ret = false;
29470         if (!id) {
29471             return ret;
29472         }
29473         Roo.each(this.allItems, function(f){
29474             if (f.id == id || f.name == id ){
29475                 ret = f;
29476                 return false;
29477             }
29478         });
29479         return ret;
29480     },
29481
29482     
29483     
29484     /**
29485      * Render this form into the passed container. This should only be called once!
29486      * @param {String/HTMLElement/Element} container The element this component should be rendered into
29487      * @return {Form} this
29488      */
29489     render : function(ct)
29490     {
29491         
29492         
29493         
29494         ct = Roo.get(ct);
29495         var o = this.autoCreate || {
29496             tag: 'form',
29497             method : this.method || 'POST',
29498             id : this.id || Roo.id()
29499         };
29500         this.initEl(ct.createChild(o));
29501
29502         this.root.render(this.el);
29503         
29504        
29505              
29506         this.items.each(function(f){
29507             f.render('x-form-el-'+f.id);
29508         });
29509
29510         if(this.buttons.length > 0){
29511             // tables are required to maintain order and for correct IE layout
29512             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
29513                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
29514                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29515             }}, null, true);
29516             var tr = tb.getElementsByTagName('tr')[0];
29517             for(var i = 0, len = this.buttons.length; i < len; i++) {
29518                 var b = this.buttons[i];
29519                 var td = document.createElement('td');
29520                 td.className = 'x-form-btn-td';
29521                 b.render(tr.appendChild(td));
29522             }
29523         }
29524         if(this.monitorValid){ // initialize after render
29525             this.startMonitoring();
29526         }
29527         this.fireEvent('rendered', this);
29528         return this;
29529     },
29530
29531     /**
29532      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
29533      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29534      * object or a valid Roo.DomHelper element config
29535      * @param {Function} handler The function called when the button is clicked
29536      * @param {Object} scope (optional) The scope of the handler function
29537      * @return {Roo.Button}
29538      */
29539     addButton : function(config, handler, scope){
29540         var bc = {
29541             handler: handler,
29542             scope: scope,
29543             minWidth: this.minButtonWidth,
29544             hideParent:true
29545         };
29546         if(typeof config == "string"){
29547             bc.text = config;
29548         }else{
29549             Roo.apply(bc, config);
29550         }
29551         var btn = new Roo.Button(null, bc);
29552         this.buttons.push(btn);
29553         return btn;
29554     },
29555
29556      /**
29557      * Adds a series of form elements (using the xtype property as the factory method.
29558      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
29559      * @param {Object} config 
29560      */
29561     
29562     addxtype : function()
29563     {
29564         var ar = Array.prototype.slice.call(arguments, 0);
29565         var ret = false;
29566         for(var i = 0; i < ar.length; i++) {
29567             if (!ar[i]) {
29568                 continue; // skip -- if this happends something invalid got sent, we 
29569                 // should ignore it, as basically that interface element will not show up
29570                 // and that should be pretty obvious!!
29571             }
29572             
29573             if (Roo.form[ar[i].xtype]) {
29574                 ar[i].form = this;
29575                 var fe = Roo.factory(ar[i], Roo.form);
29576                 if (!ret) {
29577                     ret = fe;
29578                 }
29579                 fe.form = this;
29580                 if (fe.store) {
29581                     fe.store.form = this;
29582                 }
29583                 if (fe.isLayout) {  
29584                          
29585                     this.start(fe);
29586                     this.allItems.push(fe);
29587                     if (fe.items && fe.addxtype) {
29588                         fe.addxtype.apply(fe, fe.items);
29589                         delete fe.items;
29590                     }
29591                      this.end();
29592                     continue;
29593                 }
29594                 
29595                 
29596                  
29597                 this.add(fe);
29598               //  console.log('adding ' + ar[i].xtype);
29599             }
29600             if (ar[i].xtype == 'Button') {  
29601                 //console.log('adding button');
29602                 //console.log(ar[i]);
29603                 this.addButton(ar[i]);
29604                 this.allItems.push(fe);
29605                 continue;
29606             }
29607             
29608             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
29609                 alert('end is not supported on xtype any more, use items');
29610             //    this.end();
29611             //    //console.log('adding end');
29612             }
29613             
29614         }
29615         return ret;
29616     },
29617     
29618     /**
29619      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
29620      * option "monitorValid"
29621      */
29622     startMonitoring : function(){
29623         if(!this.bound){
29624             this.bound = true;
29625             Roo.TaskMgr.start({
29626                 run : this.bindHandler,
29627                 interval : this.monitorPoll || 200,
29628                 scope: this
29629             });
29630         }
29631     },
29632
29633     /**
29634      * Stops monitoring of the valid state of this form
29635      */
29636     stopMonitoring : function(){
29637         this.bound = false;
29638     },
29639
29640     // private
29641     bindHandler : function(){
29642         if(!this.bound){
29643             return false; // stops binding
29644         }
29645         var valid = true;
29646         this.items.each(function(f){
29647             if(!f.isValid(true)){
29648                 valid = false;
29649                 return false;
29650             }
29651         });
29652         for(var i = 0, len = this.buttons.length; i < len; i++){
29653             var btn = this.buttons[i];
29654             if(btn.formBind === true && btn.disabled === valid){
29655                 btn.setDisabled(!valid);
29656             }
29657         }
29658         this.fireEvent('clientvalidation', this, valid);
29659     }
29660     
29661     
29662     
29663     
29664     
29665     
29666     
29667     
29668 });
29669
29670
29671 // back compat
29672 Roo.Form = Roo.form.Form;
29673 /*
29674  * Based on:
29675  * Ext JS Library 1.1.1
29676  * Copyright(c) 2006-2007, Ext JS, LLC.
29677  *
29678  * Originally Released Under LGPL - original licence link has changed is not relivant.
29679  *
29680  * Fork - LGPL
29681  * <script type="text/javascript">
29682  */
29683
29684 // as we use this in bootstrap.
29685 Roo.namespace('Roo.form');
29686  /**
29687  * @class Roo.form.Action
29688  * Internal Class used to handle form actions
29689  * @constructor
29690  * @param {Roo.form.BasicForm} el The form element or its id
29691  * @param {Object} config Configuration options
29692  */
29693
29694  
29695  
29696 // define the action interface
29697 Roo.form.Action = function(form, options){
29698     this.form = form;
29699     this.options = options || {};
29700 };
29701 /**
29702  * Client Validation Failed
29703  * @const 
29704  */
29705 Roo.form.Action.CLIENT_INVALID = 'client';
29706 /**
29707  * Server Validation Failed
29708  * @const 
29709  */
29710 Roo.form.Action.SERVER_INVALID = 'server';
29711  /**
29712  * Connect to Server Failed
29713  * @const 
29714  */
29715 Roo.form.Action.CONNECT_FAILURE = 'connect';
29716 /**
29717  * Reading Data from Server Failed
29718  * @const 
29719  */
29720 Roo.form.Action.LOAD_FAILURE = 'load';
29721
29722 Roo.form.Action.prototype = {
29723     type : 'default',
29724     failureType : undefined,
29725     response : undefined,
29726     result : undefined,
29727
29728     // interface method
29729     run : function(options){
29730
29731     },
29732
29733     // interface method
29734     success : function(response){
29735
29736     },
29737
29738     // interface method
29739     handleResponse : function(response){
29740
29741     },
29742
29743     // default connection failure
29744     failure : function(response){
29745         
29746         this.response = response;
29747         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29748         this.form.afterAction(this, false);
29749     },
29750
29751     processResponse : function(response){
29752         this.response = response;
29753         if(!response.responseText){
29754             return true;
29755         }
29756         this.result = this.handleResponse(response);
29757         return this.result;
29758     },
29759
29760     // utility functions used internally
29761     getUrl : function(appendParams){
29762         var url = this.options.url || this.form.url || this.form.el.dom.action;
29763         if(appendParams){
29764             var p = this.getParams();
29765             if(p){
29766                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
29767             }
29768         }
29769         return url;
29770     },
29771
29772     getMethod : function(){
29773         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
29774     },
29775
29776     getParams : function(){
29777         var bp = this.form.baseParams;
29778         var p = this.options.params;
29779         if(p){
29780             if(typeof p == "object"){
29781                 p = Roo.urlEncode(Roo.applyIf(p, bp));
29782             }else if(typeof p == 'string' && bp){
29783                 p += '&' + Roo.urlEncode(bp);
29784             }
29785         }else if(bp){
29786             p = Roo.urlEncode(bp);
29787         }
29788         return p;
29789     },
29790
29791     createCallback : function(){
29792         return {
29793             success: this.success,
29794             failure: this.failure,
29795             scope: this,
29796             timeout: (this.form.timeout*1000),
29797             upload: this.form.fileUpload ? this.success : undefined
29798         };
29799     }
29800 };
29801
29802 Roo.form.Action.Submit = function(form, options){
29803     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
29804 };
29805
29806 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
29807     type : 'submit',
29808
29809     haveProgress : false,
29810     uploadComplete : false,
29811     
29812     // uploadProgress indicator.
29813     uploadProgress : function()
29814     {
29815         if (!this.form.progressUrl) {
29816             return;
29817         }
29818         
29819         if (!this.haveProgress) {
29820             Roo.MessageBox.progress("Uploading", "Uploading");
29821         }
29822         if (this.uploadComplete) {
29823            Roo.MessageBox.hide();
29824            return;
29825         }
29826         
29827         this.haveProgress = true;
29828    
29829         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
29830         
29831         var c = new Roo.data.Connection();
29832         c.request({
29833             url : this.form.progressUrl,
29834             params: {
29835                 id : uid
29836             },
29837             method: 'GET',
29838             success : function(req){
29839                //console.log(data);
29840                 var rdata = false;
29841                 var edata;
29842                 try  {
29843                    rdata = Roo.decode(req.responseText)
29844                 } catch (e) {
29845                     Roo.log("Invalid data from server..");
29846                     Roo.log(edata);
29847                     return;
29848                 }
29849                 if (!rdata || !rdata.success) {
29850                     Roo.log(rdata);
29851                     Roo.MessageBox.alert(Roo.encode(rdata));
29852                     return;
29853                 }
29854                 var data = rdata.data;
29855                 
29856                 if (this.uploadComplete) {
29857                    Roo.MessageBox.hide();
29858                    return;
29859                 }
29860                    
29861                 if (data){
29862                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
29863                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
29864                     );
29865                 }
29866                 this.uploadProgress.defer(2000,this);
29867             },
29868        
29869             failure: function(data) {
29870                 Roo.log('progress url failed ');
29871                 Roo.log(data);
29872             },
29873             scope : this
29874         });
29875            
29876     },
29877     
29878     
29879     run : function()
29880     {
29881         // run get Values on the form, so it syncs any secondary forms.
29882         this.form.getValues();
29883         
29884         var o = this.options;
29885         var method = this.getMethod();
29886         var isPost = method == 'POST';
29887         if(o.clientValidation === false || this.form.isValid()){
29888             
29889             if (this.form.progressUrl) {
29890                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
29891                     (new Date() * 1) + '' + Math.random());
29892                     
29893             } 
29894             
29895             
29896             Roo.Ajax.request(Roo.apply(this.createCallback(), {
29897                 form:this.form.el.dom,
29898                 url:this.getUrl(!isPost),
29899                 method: method,
29900                 params:isPost ? this.getParams() : null,
29901                 isUpload: this.form.fileUpload
29902             }));
29903             
29904             this.uploadProgress();
29905
29906         }else if (o.clientValidation !== false){ // client validation failed
29907             this.failureType = Roo.form.Action.CLIENT_INVALID;
29908             this.form.afterAction(this, false);
29909         }
29910     },
29911
29912     success : function(response)
29913     {
29914         this.uploadComplete= true;
29915         if (this.haveProgress) {
29916             Roo.MessageBox.hide();
29917         }
29918         
29919         
29920         var result = this.processResponse(response);
29921         if(result === true || result.success){
29922             this.form.afterAction(this, true);
29923             return;
29924         }
29925         if(result.errors){
29926             this.form.markInvalid(result.errors);
29927             this.failureType = Roo.form.Action.SERVER_INVALID;
29928         }
29929         this.form.afterAction(this, false);
29930     },
29931     failure : function(response)
29932     {
29933         this.uploadComplete= true;
29934         if (this.haveProgress) {
29935             Roo.MessageBox.hide();
29936         }
29937         
29938         this.response = response;
29939         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29940         this.form.afterAction(this, false);
29941     },
29942     
29943     handleResponse : function(response){
29944         if(this.form.errorReader){
29945             var rs = this.form.errorReader.read(response);
29946             var errors = [];
29947             if(rs.records){
29948                 for(var i = 0, len = rs.records.length; i < len; i++) {
29949                     var r = rs.records[i];
29950                     errors[i] = r.data;
29951                 }
29952             }
29953             if(errors.length < 1){
29954                 errors = null;
29955             }
29956             return {
29957                 success : rs.success,
29958                 errors : errors
29959             };
29960         }
29961         var ret = false;
29962         try {
29963             ret = Roo.decode(response.responseText);
29964         } catch (e) {
29965             ret = {
29966                 success: false,
29967                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
29968                 errors : []
29969             };
29970         }
29971         return ret;
29972         
29973     }
29974 });
29975
29976
29977 Roo.form.Action.Load = function(form, options){
29978     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
29979     this.reader = this.form.reader;
29980 };
29981
29982 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
29983     type : 'load',
29984
29985     run : function(){
29986         
29987         Roo.Ajax.request(Roo.apply(
29988                 this.createCallback(), {
29989                     method:this.getMethod(),
29990                     url:this.getUrl(false),
29991                     params:this.getParams()
29992         }));
29993     },
29994
29995     success : function(response){
29996         
29997         var result = this.processResponse(response);
29998         if(result === true || !result.success || !result.data){
29999             this.failureType = Roo.form.Action.LOAD_FAILURE;
30000             this.form.afterAction(this, false);
30001             return;
30002         }
30003         this.form.clearInvalid();
30004         this.form.setValues(result.data);
30005         this.form.afterAction(this, true);
30006     },
30007
30008     handleResponse : function(response){
30009         if(this.form.reader){
30010             var rs = this.form.reader.read(response);
30011             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
30012             return {
30013                 success : rs.success,
30014                 data : data
30015             };
30016         }
30017         return Roo.decode(response.responseText);
30018     }
30019 });
30020
30021 Roo.form.Action.ACTION_TYPES = {
30022     'load' : Roo.form.Action.Load,
30023     'submit' : Roo.form.Action.Submit
30024 };/*
30025  * Based on:
30026  * Ext JS Library 1.1.1
30027  * Copyright(c) 2006-2007, Ext JS, LLC.
30028  *
30029  * Originally Released Under LGPL - original licence link has changed is not relivant.
30030  *
30031  * Fork - LGPL
30032  * <script type="text/javascript">
30033  */
30034  
30035 /**
30036  * @class Roo.form.Layout
30037  * @extends Roo.Component
30038  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
30039  * @constructor
30040  * @param {Object} config Configuration options
30041  */
30042 Roo.form.Layout = function(config){
30043     var xitems = [];
30044     if (config.items) {
30045         xitems = config.items;
30046         delete config.items;
30047     }
30048     Roo.form.Layout.superclass.constructor.call(this, config);
30049     this.stack = [];
30050     Roo.each(xitems, this.addxtype, this);
30051      
30052 };
30053
30054 Roo.extend(Roo.form.Layout, Roo.Component, {
30055     /**
30056      * @cfg {String/Object} autoCreate
30057      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
30058      */
30059     /**
30060      * @cfg {String/Object/Function} style
30061      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
30062      * a function which returns such a specification.
30063      */
30064     /**
30065      * @cfg {String} labelAlign
30066      * Valid values are "left," "top" and "right" (defaults to "left")
30067      */
30068     /**
30069      * @cfg {Number} labelWidth
30070      * Fixed width in pixels of all field labels (defaults to undefined)
30071      */
30072     /**
30073      * @cfg {Boolean} clear
30074      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
30075      */
30076     clear : true,
30077     /**
30078      * @cfg {String} labelSeparator
30079      * The separator to use after field labels (defaults to ':')
30080      */
30081     labelSeparator : ':',
30082     /**
30083      * @cfg {Boolean} hideLabels
30084      * True to suppress the display of field labels in this layout (defaults to false)
30085      */
30086     hideLabels : false,
30087
30088     // private
30089     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
30090     
30091     isLayout : true,
30092     
30093     // private
30094     onRender : function(ct, position){
30095         if(this.el){ // from markup
30096             this.el = Roo.get(this.el);
30097         }else {  // generate
30098             var cfg = this.getAutoCreate();
30099             this.el = ct.createChild(cfg, position);
30100         }
30101         if(this.style){
30102             this.el.applyStyles(this.style);
30103         }
30104         if(this.labelAlign){
30105             this.el.addClass('x-form-label-'+this.labelAlign);
30106         }
30107         if(this.hideLabels){
30108             this.labelStyle = "display:none";
30109             this.elementStyle = "padding-left:0;";
30110         }else{
30111             if(typeof this.labelWidth == 'number'){
30112                 this.labelStyle = "width:"+this.labelWidth+"px;";
30113                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
30114             }
30115             if(this.labelAlign == 'top'){
30116                 this.labelStyle = "width:auto;";
30117                 this.elementStyle = "padding-left:0;";
30118             }
30119         }
30120         var stack = this.stack;
30121         var slen = stack.length;
30122         if(slen > 0){
30123             if(!this.fieldTpl){
30124                 var t = new Roo.Template(
30125                     '<div class="x-form-item {5}">',
30126                         '<label for="{0}" style="{2}">{1}{4}</label>',
30127                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30128                         '</div>',
30129                     '</div><div class="x-form-clear-left"></div>'
30130                 );
30131                 t.disableFormats = true;
30132                 t.compile();
30133                 Roo.form.Layout.prototype.fieldTpl = t;
30134             }
30135             for(var i = 0; i < slen; i++) {
30136                 if(stack[i].isFormField){
30137                     this.renderField(stack[i]);
30138                 }else{
30139                     this.renderComponent(stack[i]);
30140                 }
30141             }
30142         }
30143         if(this.clear){
30144             this.el.createChild({cls:'x-form-clear'});
30145         }
30146     },
30147
30148     // private
30149     renderField : function(f){
30150         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
30151                f.id, //0
30152                f.fieldLabel, //1
30153                f.labelStyle||this.labelStyle||'', //2
30154                this.elementStyle||'', //3
30155                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
30156                f.itemCls||this.itemCls||''  //5
30157        ], true).getPrevSibling());
30158     },
30159
30160     // private
30161     renderComponent : function(c){
30162         c.render(c.isLayout ? this.el : this.el.createChild());    
30163     },
30164     /**
30165      * Adds a object form elements (using the xtype property as the factory method.)
30166      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
30167      * @param {Object} config 
30168      */
30169     addxtype : function(o)
30170     {
30171         // create the lement.
30172         o.form = this.form;
30173         var fe = Roo.factory(o, Roo.form);
30174         this.form.allItems.push(fe);
30175         this.stack.push(fe);
30176         
30177         if (fe.isFormField) {
30178             this.form.items.add(fe);
30179         }
30180          
30181         return fe;
30182     }
30183 });
30184
30185 /**
30186  * @class Roo.form.Column
30187  * @extends Roo.form.Layout
30188  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
30189  * @constructor
30190  * @param {Object} config Configuration options
30191  */
30192 Roo.form.Column = function(config){
30193     Roo.form.Column.superclass.constructor.call(this, config);
30194 };
30195
30196 Roo.extend(Roo.form.Column, Roo.form.Layout, {
30197     /**
30198      * @cfg {Number/String} width
30199      * The fixed width of the column in pixels or CSS value (defaults to "auto")
30200      */
30201     /**
30202      * @cfg {String/Object} autoCreate
30203      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
30204      */
30205
30206     // private
30207     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
30208
30209     // private
30210     onRender : function(ct, position){
30211         Roo.form.Column.superclass.onRender.call(this, ct, position);
30212         if(this.width){
30213             this.el.setWidth(this.width);
30214         }
30215     }
30216 });
30217
30218
30219 /**
30220  * @class Roo.form.Row
30221  * @extends Roo.form.Layout
30222  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
30223  * @constructor
30224  * @param {Object} config Configuration options
30225  */
30226
30227  
30228 Roo.form.Row = function(config){
30229     Roo.form.Row.superclass.constructor.call(this, config);
30230 };
30231  
30232 Roo.extend(Roo.form.Row, Roo.form.Layout, {
30233       /**
30234      * @cfg {Number/String} width
30235      * The fixed width of the column in pixels or CSS value (defaults to "auto")
30236      */
30237     /**
30238      * @cfg {Number/String} height
30239      * The fixed height of the column in pixels or CSS value (defaults to "auto")
30240      */
30241     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
30242     
30243     padWidth : 20,
30244     // private
30245     onRender : function(ct, position){
30246         //console.log('row render');
30247         if(!this.rowTpl){
30248             var t = new Roo.Template(
30249                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
30250                     '<label for="{0}" style="{2}">{1}{4}</label>',
30251                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30252                     '</div>',
30253                 '</div>'
30254             );
30255             t.disableFormats = true;
30256             t.compile();
30257             Roo.form.Layout.prototype.rowTpl = t;
30258         }
30259         this.fieldTpl = this.rowTpl;
30260         
30261         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
30262         var labelWidth = 100;
30263         
30264         if ((this.labelAlign != 'top')) {
30265             if (typeof this.labelWidth == 'number') {
30266                 labelWidth = this.labelWidth
30267             }
30268             this.padWidth =  20 + labelWidth;
30269             
30270         }
30271         
30272         Roo.form.Column.superclass.onRender.call(this, ct, position);
30273         if(this.width){
30274             this.el.setWidth(this.width);
30275         }
30276         if(this.height){
30277             this.el.setHeight(this.height);
30278         }
30279     },
30280     
30281     // private
30282     renderField : function(f){
30283         f.fieldEl = this.fieldTpl.append(this.el, [
30284                f.id, f.fieldLabel,
30285                f.labelStyle||this.labelStyle||'',
30286                this.elementStyle||'',
30287                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
30288                f.itemCls||this.itemCls||'',
30289                f.width ? f.width + this.padWidth : 160 + this.padWidth
30290        ],true);
30291     }
30292 });
30293  
30294
30295 /**
30296  * @class Roo.form.FieldSet
30297  * @extends Roo.form.Layout
30298  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
30299  * @constructor
30300  * @param {Object} config Configuration options
30301  */
30302 Roo.form.FieldSet = function(config){
30303     Roo.form.FieldSet.superclass.constructor.call(this, config);
30304 };
30305
30306 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
30307     /**
30308      * @cfg {String} legend
30309      * The text to display as the legend for the FieldSet (defaults to '')
30310      */
30311     /**
30312      * @cfg {String/Object} autoCreate
30313      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
30314      */
30315
30316     // private
30317     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
30318
30319     // private
30320     onRender : function(ct, position){
30321         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
30322         if(this.legend){
30323             this.setLegend(this.legend);
30324         }
30325     },
30326
30327     // private
30328     setLegend : function(text){
30329         if(this.rendered){
30330             this.el.child('legend').update(text);
30331         }
30332     }
30333 });/*
30334  * Based on:
30335  * Ext JS Library 1.1.1
30336  * Copyright(c) 2006-2007, Ext JS, LLC.
30337  *
30338  * Originally Released Under LGPL - original licence link has changed is not relivant.
30339  *
30340  * Fork - LGPL
30341  * <script type="text/javascript">
30342  */
30343 /**
30344  * @class Roo.form.VTypes
30345  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
30346  * @singleton
30347  */
30348 Roo.form.VTypes = function(){
30349     // closure these in so they are only created once.
30350     var alpha = /^[a-zA-Z_]+$/;
30351     var alphanum = /^[a-zA-Z0-9_]+$/;
30352     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
30353     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
30354
30355     // All these messages and functions are configurable
30356     return {
30357         /**
30358          * The function used to validate email addresses
30359          * @param {String} value The email address
30360          */
30361         'email' : function(v){
30362             return email.test(v);
30363         },
30364         /**
30365          * The error text to display when the email validation function returns false
30366          * @type String
30367          */
30368         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
30369         /**
30370          * The keystroke filter mask to be applied on email input
30371          * @type RegExp
30372          */
30373         'emailMask' : /[a-z0-9_\.\-@]/i,
30374
30375         /**
30376          * The function used to validate URLs
30377          * @param {String} value The URL
30378          */
30379         'url' : function(v){
30380             return url.test(v);
30381         },
30382         /**
30383          * The error text to display when the url validation function returns false
30384          * @type String
30385          */
30386         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
30387         
30388         /**
30389          * The function used to validate alpha values
30390          * @param {String} value The value
30391          */
30392         'alpha' : function(v){
30393             return alpha.test(v);
30394         },
30395         /**
30396          * The error text to display when the alpha validation function returns false
30397          * @type String
30398          */
30399         'alphaText' : 'This field should only contain letters and _',
30400         /**
30401          * The keystroke filter mask to be applied on alpha input
30402          * @type RegExp
30403          */
30404         'alphaMask' : /[a-z_]/i,
30405
30406         /**
30407          * The function used to validate alphanumeric values
30408          * @param {String} value The value
30409          */
30410         'alphanum' : function(v){
30411             return alphanum.test(v);
30412         },
30413         /**
30414          * The error text to display when the alphanumeric validation function returns false
30415          * @type String
30416          */
30417         'alphanumText' : 'This field should only contain letters, numbers and _',
30418         /**
30419          * The keystroke filter mask to be applied on alphanumeric input
30420          * @type RegExp
30421          */
30422         'alphanumMask' : /[a-z0-9_]/i
30423     };
30424 }();//<script type="text/javascript">
30425
30426 /**
30427  * @class Roo.form.FCKeditor
30428  * @extends Roo.form.TextArea
30429  * Wrapper around the FCKEditor http://www.fckeditor.net
30430  * @constructor
30431  * Creates a new FCKeditor
30432  * @param {Object} config Configuration options
30433  */
30434 Roo.form.FCKeditor = function(config){
30435     Roo.form.FCKeditor.superclass.constructor.call(this, config);
30436     this.addEvents({
30437          /**
30438          * @event editorinit
30439          * Fired when the editor is initialized - you can add extra handlers here..
30440          * @param {FCKeditor} this
30441          * @param {Object} the FCK object.
30442          */
30443         editorinit : true
30444     });
30445     
30446     
30447 };
30448 Roo.form.FCKeditor.editors = { };
30449 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
30450 {
30451     //defaultAutoCreate : {
30452     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
30453     //},
30454     // private
30455     /**
30456      * @cfg {Object} fck options - see fck manual for details.
30457      */
30458     fckconfig : false,
30459     
30460     /**
30461      * @cfg {Object} fck toolbar set (Basic or Default)
30462      */
30463     toolbarSet : 'Basic',
30464     /**
30465      * @cfg {Object} fck BasePath
30466      */ 
30467     basePath : '/fckeditor/',
30468     
30469     
30470     frame : false,
30471     
30472     value : '',
30473     
30474    
30475     onRender : function(ct, position)
30476     {
30477         if(!this.el){
30478             this.defaultAutoCreate = {
30479                 tag: "textarea",
30480                 style:"width:300px;height:60px;",
30481                 autocomplete: "new-password"
30482             };
30483         }
30484         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
30485         /*
30486         if(this.grow){
30487             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
30488             if(this.preventScrollbars){
30489                 this.el.setStyle("overflow", "hidden");
30490             }
30491             this.el.setHeight(this.growMin);
30492         }
30493         */
30494         //console.log('onrender' + this.getId() );
30495         Roo.form.FCKeditor.editors[this.getId()] = this;
30496          
30497
30498         this.replaceTextarea() ;
30499         
30500     },
30501     
30502     getEditor : function() {
30503         return this.fckEditor;
30504     },
30505     /**
30506      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
30507      * @param {Mixed} value The value to set
30508      */
30509     
30510     
30511     setValue : function(value)
30512     {
30513         //console.log('setValue: ' + value);
30514         
30515         if(typeof(value) == 'undefined') { // not sure why this is happending...
30516             return;
30517         }
30518         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
30519         
30520         //if(!this.el || !this.getEditor()) {
30521         //    this.value = value;
30522             //this.setValue.defer(100,this,[value]);    
30523         //    return;
30524         //} 
30525         
30526         if(!this.getEditor()) {
30527             return;
30528         }
30529         
30530         this.getEditor().SetData(value);
30531         
30532         //
30533
30534     },
30535
30536     /**
30537      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
30538      * @return {Mixed} value The field value
30539      */
30540     getValue : function()
30541     {
30542         
30543         if (this.frame && this.frame.dom.style.display == 'none') {
30544             return Roo.form.FCKeditor.superclass.getValue.call(this);
30545         }
30546         
30547         if(!this.el || !this.getEditor()) {
30548            
30549            // this.getValue.defer(100,this); 
30550             return this.value;
30551         }
30552        
30553         
30554         var value=this.getEditor().GetData();
30555         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
30556         return Roo.form.FCKeditor.superclass.getValue.call(this);
30557         
30558
30559     },
30560
30561     /**
30562      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
30563      * @return {Mixed} value The field value
30564      */
30565     getRawValue : function()
30566     {
30567         if (this.frame && this.frame.dom.style.display == 'none') {
30568             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30569         }
30570         
30571         if(!this.el || !this.getEditor()) {
30572             //this.getRawValue.defer(100,this); 
30573             return this.value;
30574             return;
30575         }
30576         
30577         
30578         
30579         var value=this.getEditor().GetData();
30580         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
30581         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30582          
30583     },
30584     
30585     setSize : function(w,h) {
30586         
30587         
30588         
30589         //if (this.frame && this.frame.dom.style.display == 'none') {
30590         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30591         //    return;
30592         //}
30593         //if(!this.el || !this.getEditor()) {
30594         //    this.setSize.defer(100,this, [w,h]); 
30595         //    return;
30596         //}
30597         
30598         
30599         
30600         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30601         
30602         this.frame.dom.setAttribute('width', w);
30603         this.frame.dom.setAttribute('height', h);
30604         this.frame.setSize(w,h);
30605         
30606     },
30607     
30608     toggleSourceEdit : function(value) {
30609         
30610       
30611          
30612         this.el.dom.style.display = value ? '' : 'none';
30613         this.frame.dom.style.display = value ?  'none' : '';
30614         
30615     },
30616     
30617     
30618     focus: function(tag)
30619     {
30620         if (this.frame.dom.style.display == 'none') {
30621             return Roo.form.FCKeditor.superclass.focus.call(this);
30622         }
30623         if(!this.el || !this.getEditor()) {
30624             this.focus.defer(100,this, [tag]); 
30625             return;
30626         }
30627         
30628         
30629         
30630         
30631         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
30632         this.getEditor().Focus();
30633         if (tgs.length) {
30634             if (!this.getEditor().Selection.GetSelection()) {
30635                 this.focus.defer(100,this, [tag]); 
30636                 return;
30637             }
30638             
30639             
30640             var r = this.getEditor().EditorDocument.createRange();
30641             r.setStart(tgs[0],0);
30642             r.setEnd(tgs[0],0);
30643             this.getEditor().Selection.GetSelection().removeAllRanges();
30644             this.getEditor().Selection.GetSelection().addRange(r);
30645             this.getEditor().Focus();
30646         }
30647         
30648     },
30649     
30650     
30651     
30652     replaceTextarea : function()
30653     {
30654         if ( document.getElementById( this.getId() + '___Frame' ) ) {
30655             return ;
30656         }
30657         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
30658         //{
30659             // We must check the elements firstly using the Id and then the name.
30660         var oTextarea = document.getElementById( this.getId() );
30661         
30662         var colElementsByName = document.getElementsByName( this.getId() ) ;
30663          
30664         oTextarea.style.display = 'none' ;
30665
30666         if ( oTextarea.tabIndex ) {            
30667             this.TabIndex = oTextarea.tabIndex ;
30668         }
30669         
30670         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
30671         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
30672         this.frame = Roo.get(this.getId() + '___Frame')
30673     },
30674     
30675     _getConfigHtml : function()
30676     {
30677         var sConfig = '' ;
30678
30679         for ( var o in this.fckconfig ) {
30680             sConfig += sConfig.length > 0  ? '&amp;' : '';
30681             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
30682         }
30683
30684         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
30685     },
30686     
30687     
30688     _getIFrameHtml : function()
30689     {
30690         var sFile = 'fckeditor.html' ;
30691         /* no idea what this is about..
30692         try
30693         {
30694             if ( (/fcksource=true/i).test( window.top.location.search ) )
30695                 sFile = 'fckeditor.original.html' ;
30696         }
30697         catch (e) { 
30698         */
30699
30700         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
30701         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
30702         
30703         
30704         var html = '<iframe id="' + this.getId() +
30705             '___Frame" src="' + sLink +
30706             '" width="' + this.width +
30707             '" height="' + this.height + '"' +
30708             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
30709             ' frameborder="0" scrolling="no"></iframe>' ;
30710
30711         return html ;
30712     },
30713     
30714     _insertHtmlBefore : function( html, element )
30715     {
30716         if ( element.insertAdjacentHTML )       {
30717             // IE
30718             element.insertAdjacentHTML( 'beforeBegin', html ) ;
30719         } else { // Gecko
30720             var oRange = document.createRange() ;
30721             oRange.setStartBefore( element ) ;
30722             var oFragment = oRange.createContextualFragment( html );
30723             element.parentNode.insertBefore( oFragment, element ) ;
30724         }
30725     }
30726     
30727     
30728   
30729     
30730     
30731     
30732     
30733
30734 });
30735
30736 //Roo.reg('fckeditor', Roo.form.FCKeditor);
30737
30738 function FCKeditor_OnComplete(editorInstance){
30739     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
30740     f.fckEditor = editorInstance;
30741     //console.log("loaded");
30742     f.fireEvent('editorinit', f, editorInstance);
30743
30744   
30745
30746  
30747
30748
30749
30750
30751
30752
30753
30754
30755
30756
30757
30758
30759
30760
30761
30762 //<script type="text/javascript">
30763 /**
30764  * @class Roo.form.GridField
30765  * @extends Roo.form.Field
30766  * Embed a grid (or editable grid into a form)
30767  * STATUS ALPHA
30768  * 
30769  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
30770  * it needs 
30771  * xgrid.store = Roo.data.Store
30772  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
30773  * xgrid.store.reader = Roo.data.JsonReader 
30774  * 
30775  * 
30776  * @constructor
30777  * Creates a new GridField
30778  * @param {Object} config Configuration options
30779  */
30780 Roo.form.GridField = function(config){
30781     Roo.form.GridField.superclass.constructor.call(this, config);
30782      
30783 };
30784
30785 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
30786     /**
30787      * @cfg {Number} width  - used to restrict width of grid..
30788      */
30789     width : 100,
30790     /**
30791      * @cfg {Number} height - used to restrict height of grid..
30792      */
30793     height : 50,
30794      /**
30795      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
30796          * 
30797          *}
30798      */
30799     xgrid : false, 
30800     /**
30801      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30802      * {tag: "input", type: "checkbox", autocomplete: "off"})
30803      */
30804    // defaultAutoCreate : { tag: 'div' },
30805     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
30806     /**
30807      * @cfg {String} addTitle Text to include for adding a title.
30808      */
30809     addTitle : false,
30810     //
30811     onResize : function(){
30812         Roo.form.Field.superclass.onResize.apply(this, arguments);
30813     },
30814
30815     initEvents : function(){
30816         // Roo.form.Checkbox.superclass.initEvents.call(this);
30817         // has no events...
30818        
30819     },
30820
30821
30822     getResizeEl : function(){
30823         return this.wrap;
30824     },
30825
30826     getPositionEl : function(){
30827         return this.wrap;
30828     },
30829
30830     // private
30831     onRender : function(ct, position){
30832         
30833         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
30834         var style = this.style;
30835         delete this.style;
30836         
30837         Roo.form.GridField.superclass.onRender.call(this, ct, position);
30838         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
30839         this.viewEl = this.wrap.createChild({ tag: 'div' });
30840         if (style) {
30841             this.viewEl.applyStyles(style);
30842         }
30843         if (this.width) {
30844             this.viewEl.setWidth(this.width);
30845         }
30846         if (this.height) {
30847             this.viewEl.setHeight(this.height);
30848         }
30849         //if(this.inputValue !== undefined){
30850         //this.setValue(this.value);
30851         
30852         
30853         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
30854         
30855         
30856         this.grid.render();
30857         this.grid.getDataSource().on('remove', this.refreshValue, this);
30858         this.grid.getDataSource().on('update', this.refreshValue, this);
30859         this.grid.on('afteredit', this.refreshValue, this);
30860  
30861     },
30862      
30863     
30864     /**
30865      * Sets the value of the item. 
30866      * @param {String} either an object  or a string..
30867      */
30868     setValue : function(v){
30869         //this.value = v;
30870         v = v || []; // empty set..
30871         // this does not seem smart - it really only affects memoryproxy grids..
30872         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
30873             var ds = this.grid.getDataSource();
30874             // assumes a json reader..
30875             var data = {}
30876             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
30877             ds.loadData( data);
30878         }
30879         // clear selection so it does not get stale.
30880         if (this.grid.sm) { 
30881             this.grid.sm.clearSelections();
30882         }
30883         
30884         Roo.form.GridField.superclass.setValue.call(this, v);
30885         this.refreshValue();
30886         // should load data in the grid really....
30887     },
30888     
30889     // private
30890     refreshValue: function() {
30891          var val = [];
30892         this.grid.getDataSource().each(function(r) {
30893             val.push(r.data);
30894         });
30895         this.el.dom.value = Roo.encode(val);
30896     }
30897     
30898      
30899     
30900     
30901 });/*
30902  * Based on:
30903  * Ext JS Library 1.1.1
30904  * Copyright(c) 2006-2007, Ext JS, LLC.
30905  *
30906  * Originally Released Under LGPL - original licence link has changed is not relivant.
30907  *
30908  * Fork - LGPL
30909  * <script type="text/javascript">
30910  */
30911 /**
30912  * @class Roo.form.DisplayField
30913  * @extends Roo.form.Field
30914  * A generic Field to display non-editable data.
30915  * @cfg {Boolean} closable (true|false) default false
30916  * @constructor
30917  * Creates a new Display Field item.
30918  * @param {Object} config Configuration options
30919  */
30920 Roo.form.DisplayField = function(config){
30921     Roo.form.DisplayField.superclass.constructor.call(this, config);
30922     
30923     this.addEvents({
30924         /**
30925          * @event close
30926          * Fires after the click the close btn
30927              * @param {Roo.form.DisplayField} this
30928              */
30929         close : true
30930     });
30931 };
30932
30933 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
30934     inputType:      'hidden',
30935     allowBlank:     true,
30936     readOnly:         true,
30937     
30938  
30939     /**
30940      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30941      */
30942     focusClass : undefined,
30943     /**
30944      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30945      */
30946     fieldClass: 'x-form-field',
30947     
30948      /**
30949      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
30950      */
30951     valueRenderer: undefined,
30952     
30953     width: 100,
30954     /**
30955      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30956      * {tag: "input", type: "checkbox", autocomplete: "off"})
30957      */
30958      
30959  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30960  
30961     closable : false,
30962     
30963     onResize : function(){
30964         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
30965         
30966     },
30967
30968     initEvents : function(){
30969         // Roo.form.Checkbox.superclass.initEvents.call(this);
30970         // has no events...
30971         
30972         if(this.closable){
30973             this.closeEl.on('click', this.onClose, this);
30974         }
30975        
30976     },
30977
30978
30979     getResizeEl : function(){
30980         return this.wrap;
30981     },
30982
30983     getPositionEl : function(){
30984         return this.wrap;
30985     },
30986
30987     // private
30988     onRender : function(ct, position){
30989         
30990         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
30991         //if(this.inputValue !== undefined){
30992         this.wrap = this.el.wrap();
30993         
30994         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
30995         
30996         if(this.closable){
30997             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
30998         }
30999         
31000         if (this.bodyStyle) {
31001             this.viewEl.applyStyles(this.bodyStyle);
31002         }
31003         //this.viewEl.setStyle('padding', '2px');
31004         
31005         this.setValue(this.value);
31006         
31007     },
31008 /*
31009     // private
31010     initValue : Roo.emptyFn,
31011
31012   */
31013
31014         // private
31015     onClick : function(){
31016         
31017     },
31018
31019     /**
31020      * Sets the checked state of the checkbox.
31021      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
31022      */
31023     setValue : function(v){
31024         this.value = v;
31025         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
31026         // this might be called before we have a dom element..
31027         if (!this.viewEl) {
31028             return;
31029         }
31030         this.viewEl.dom.innerHTML = html;
31031         Roo.form.DisplayField.superclass.setValue.call(this, v);
31032
31033     },
31034     
31035     onClose : function(e)
31036     {
31037         e.preventDefault();
31038         
31039         this.fireEvent('close', this);
31040     }
31041 });/*
31042  * 
31043  * Licence- LGPL
31044  * 
31045  */
31046
31047 /**
31048  * @class Roo.form.DayPicker
31049  * @extends Roo.form.Field
31050  * A Day picker show [M] [T] [W] ....
31051  * @constructor
31052  * Creates a new Day Picker
31053  * @param {Object} config Configuration options
31054  */
31055 Roo.form.DayPicker= function(config){
31056     Roo.form.DayPicker.superclass.constructor.call(this, config);
31057      
31058 };
31059
31060 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
31061     /**
31062      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
31063      */
31064     focusClass : undefined,
31065     /**
31066      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
31067      */
31068     fieldClass: "x-form-field",
31069    
31070     /**
31071      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
31072      * {tag: "input", type: "checkbox", autocomplete: "off"})
31073      */
31074     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
31075     
31076    
31077     actionMode : 'viewEl', 
31078     //
31079     // private
31080  
31081     inputType : 'hidden',
31082     
31083      
31084     inputElement: false, // real input element?
31085     basedOn: false, // ????
31086     
31087     isFormField: true, // not sure where this is needed!!!!
31088
31089     onResize : function(){
31090         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
31091         if(!this.boxLabel){
31092             this.el.alignTo(this.wrap, 'c-c');
31093         }
31094     },
31095
31096     initEvents : function(){
31097         Roo.form.Checkbox.superclass.initEvents.call(this);
31098         this.el.on("click", this.onClick,  this);
31099         this.el.on("change", this.onClick,  this);
31100     },
31101
31102
31103     getResizeEl : function(){
31104         return this.wrap;
31105     },
31106
31107     getPositionEl : function(){
31108         return this.wrap;
31109     },
31110
31111     
31112     // private
31113     onRender : function(ct, position){
31114         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
31115        
31116         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
31117         
31118         var r1 = '<table><tr>';
31119         var r2 = '<tr class="x-form-daypick-icons">';
31120         for (var i=0; i < 7; i++) {
31121             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
31122             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
31123         }
31124         
31125         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
31126         viewEl.select('img').on('click', this.onClick, this);
31127         this.viewEl = viewEl;   
31128         
31129         
31130         // this will not work on Chrome!!!
31131         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
31132         this.el.on('propertychange', this.setFromHidden,  this);  //ie
31133         
31134         
31135           
31136
31137     },
31138
31139     // private
31140     initValue : Roo.emptyFn,
31141
31142     /**
31143      * Returns the checked state of the checkbox.
31144      * @return {Boolean} True if checked, else false
31145      */
31146     getValue : function(){
31147         return this.el.dom.value;
31148         
31149     },
31150
31151         // private
31152     onClick : function(e){ 
31153         //this.setChecked(!this.checked);
31154         Roo.get(e.target).toggleClass('x-menu-item-checked');
31155         this.refreshValue();
31156         //if(this.el.dom.checked != this.checked){
31157         //    this.setValue(this.el.dom.checked);
31158        // }
31159     },
31160     
31161     // private
31162     refreshValue : function()
31163     {
31164         var val = '';
31165         this.viewEl.select('img',true).each(function(e,i,n)  {
31166             val += e.is(".x-menu-item-checked") ? String(n) : '';
31167         });
31168         this.setValue(val, true);
31169     },
31170
31171     /**
31172      * Sets the checked state of the checkbox.
31173      * On is always based on a string comparison between inputValue and the param.
31174      * @param {Boolean/String} value - the value to set 
31175      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
31176      */
31177     setValue : function(v,suppressEvent){
31178         if (!this.el.dom) {
31179             return;
31180         }
31181         var old = this.el.dom.value ;
31182         this.el.dom.value = v;
31183         if (suppressEvent) {
31184             return ;
31185         }
31186          
31187         // update display..
31188         this.viewEl.select('img',true).each(function(e,i,n)  {
31189             
31190             var on = e.is(".x-menu-item-checked");
31191             var newv = v.indexOf(String(n)) > -1;
31192             if (on != newv) {
31193                 e.toggleClass('x-menu-item-checked');
31194             }
31195             
31196         });
31197         
31198         
31199         this.fireEvent('change', this, v, old);
31200         
31201         
31202     },
31203    
31204     // handle setting of hidden value by some other method!!?!?
31205     setFromHidden: function()
31206     {
31207         if(!this.el){
31208             return;
31209         }
31210         //console.log("SET FROM HIDDEN");
31211         //alert('setFrom hidden');
31212         this.setValue(this.el.dom.value);
31213     },
31214     
31215     onDestroy : function()
31216     {
31217         if(this.viewEl){
31218             Roo.get(this.viewEl).remove();
31219         }
31220          
31221         Roo.form.DayPicker.superclass.onDestroy.call(this);
31222     }
31223
31224 });/*
31225  * RooJS Library 1.1.1
31226  * Copyright(c) 2008-2011  Alan Knowles
31227  *
31228  * License - LGPL
31229  */
31230  
31231
31232 /**
31233  * @class Roo.form.ComboCheck
31234  * @extends Roo.form.ComboBox
31235  * A combobox for multiple select items.
31236  *
31237  * FIXME - could do with a reset button..
31238  * 
31239  * @constructor
31240  * Create a new ComboCheck
31241  * @param {Object} config Configuration options
31242  */
31243 Roo.form.ComboCheck = function(config){
31244     Roo.form.ComboCheck.superclass.constructor.call(this, config);
31245     // should verify some data...
31246     // like
31247     // hiddenName = required..
31248     // displayField = required
31249     // valudField == required
31250     var req= [ 'hiddenName', 'displayField', 'valueField' ];
31251     var _t = this;
31252     Roo.each(req, function(e) {
31253         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
31254             throw "Roo.form.ComboCheck : missing value for: " + e;
31255         }
31256     });
31257     
31258     
31259 };
31260
31261 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
31262      
31263      
31264     editable : false,
31265      
31266     selectedClass: 'x-menu-item-checked', 
31267     
31268     // private
31269     onRender : function(ct, position){
31270         var _t = this;
31271         
31272         
31273         
31274         if(!this.tpl){
31275             var cls = 'x-combo-list';
31276
31277             
31278             this.tpl =  new Roo.Template({
31279                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
31280                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
31281                    '<span>{' + this.displayField + '}</span>' +
31282                     '</div>' 
31283                 
31284             });
31285         }
31286  
31287         
31288         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
31289         this.view.singleSelect = false;
31290         this.view.multiSelect = true;
31291         this.view.toggleSelect = true;
31292         this.pageTb.add(new Roo.Toolbar.Fill(), {
31293             
31294             text: 'Done',
31295             handler: function()
31296             {
31297                 _t.collapse();
31298             }
31299         });
31300     },
31301     
31302     onViewOver : function(e, t){
31303         // do nothing...
31304         return;
31305         
31306     },
31307     
31308     onViewClick : function(doFocus,index){
31309         return;
31310         
31311     },
31312     select: function () {
31313         //Roo.log("SELECT CALLED");
31314     },
31315      
31316     selectByValue : function(xv, scrollIntoView){
31317         var ar = this.getValueArray();
31318         var sels = [];
31319         
31320         Roo.each(ar, function(v) {
31321             if(v === undefined || v === null){
31322                 return;
31323             }
31324             var r = this.findRecord(this.valueField, v);
31325             if(r){
31326                 sels.push(this.store.indexOf(r))
31327                 
31328             }
31329         },this);
31330         this.view.select(sels);
31331         return false;
31332     },
31333     
31334     
31335     
31336     onSelect : function(record, index){
31337        // Roo.log("onselect Called");
31338        // this is only called by the clear button now..
31339         this.view.clearSelections();
31340         this.setValue('[]');
31341         if (this.value != this.valueBefore) {
31342             this.fireEvent('change', this, this.value, this.valueBefore);
31343             this.valueBefore = this.value;
31344         }
31345     },
31346     getValueArray : function()
31347     {
31348         var ar = [] ;
31349         
31350         try {
31351             //Roo.log(this.value);
31352             if (typeof(this.value) == 'undefined') {
31353                 return [];
31354             }
31355             var ar = Roo.decode(this.value);
31356             return  ar instanceof Array ? ar : []; //?? valid?
31357             
31358         } catch(e) {
31359             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
31360             return [];
31361         }
31362          
31363     },
31364     expand : function ()
31365     {
31366         
31367         Roo.form.ComboCheck.superclass.expand.call(this);
31368         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
31369         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
31370         
31371
31372     },
31373     
31374     collapse : function(){
31375         Roo.form.ComboCheck.superclass.collapse.call(this);
31376         var sl = this.view.getSelectedIndexes();
31377         var st = this.store;
31378         var nv = [];
31379         var tv = [];
31380         var r;
31381         Roo.each(sl, function(i) {
31382             r = st.getAt(i);
31383             nv.push(r.get(this.valueField));
31384         },this);
31385         this.setValue(Roo.encode(nv));
31386         if (this.value != this.valueBefore) {
31387
31388             this.fireEvent('change', this, this.value, this.valueBefore);
31389             this.valueBefore = this.value;
31390         }
31391         
31392     },
31393     
31394     setValue : function(v){
31395         // Roo.log(v);
31396         this.value = v;
31397         
31398         var vals = this.getValueArray();
31399         var tv = [];
31400         Roo.each(vals, function(k) {
31401             var r = this.findRecord(this.valueField, k);
31402             if(r){
31403                 tv.push(r.data[this.displayField]);
31404             }else if(this.valueNotFoundText !== undefined){
31405                 tv.push( this.valueNotFoundText );
31406             }
31407         },this);
31408        // Roo.log(tv);
31409         
31410         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
31411         this.hiddenField.value = v;
31412         this.value = v;
31413     }
31414     
31415 });/*
31416  * Based on:
31417  * Ext JS Library 1.1.1
31418  * Copyright(c) 2006-2007, Ext JS, LLC.
31419  *
31420  * Originally Released Under LGPL - original licence link has changed is not relivant.
31421  *
31422  * Fork - LGPL
31423  * <script type="text/javascript">
31424  */
31425  
31426 /**
31427  * @class Roo.form.Signature
31428  * @extends Roo.form.Field
31429  * Signature field.  
31430  * @constructor
31431  * 
31432  * @param {Object} config Configuration options
31433  */
31434
31435 Roo.form.Signature = function(config){
31436     Roo.form.Signature.superclass.constructor.call(this, config);
31437     
31438     this.addEvents({// not in used??
31439          /**
31440          * @event confirm
31441          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
31442              * @param {Roo.form.Signature} combo This combo box
31443              */
31444         'confirm' : true,
31445         /**
31446          * @event reset
31447          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
31448              * @param {Roo.form.ComboBox} combo This combo box
31449              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
31450              */
31451         'reset' : true
31452     });
31453 };
31454
31455 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
31456     /**
31457      * @cfg {Object} labels Label to use when rendering a form.
31458      * defaults to 
31459      * labels : { 
31460      *      clear : "Clear",
31461      *      confirm : "Confirm"
31462      *  }
31463      */
31464     labels : { 
31465         clear : "Clear",
31466         confirm : "Confirm"
31467     },
31468     /**
31469      * @cfg {Number} width The signature panel width (defaults to 300)
31470      */
31471     width: 300,
31472     /**
31473      * @cfg {Number} height The signature panel height (defaults to 100)
31474      */
31475     height : 100,
31476     /**
31477      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
31478      */
31479     allowBlank : false,
31480     
31481     //private
31482     // {Object} signPanel The signature SVG panel element (defaults to {})
31483     signPanel : {},
31484     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
31485     isMouseDown : false,
31486     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
31487     isConfirmed : false,
31488     // {String} signatureTmp SVG mapping string (defaults to empty string)
31489     signatureTmp : '',
31490     
31491     
31492     defaultAutoCreate : { // modified by initCompnoent..
31493         tag: "input",
31494         type:"hidden"
31495     },
31496
31497     // private
31498     onRender : function(ct, position){
31499         
31500         Roo.form.Signature.superclass.onRender.call(this, ct, position);
31501         
31502         this.wrap = this.el.wrap({
31503             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
31504         });
31505         
31506         this.createToolbar(this);
31507         this.signPanel = this.wrap.createChild({
31508                 tag: 'div',
31509                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
31510             }, this.el
31511         );
31512             
31513         this.svgID = Roo.id();
31514         this.svgEl = this.signPanel.createChild({
31515               xmlns : 'http://www.w3.org/2000/svg',
31516               tag : 'svg',
31517               id : this.svgID + "-svg",
31518               width: this.width,
31519               height: this.height,
31520               viewBox: '0 0 '+this.width+' '+this.height,
31521               cn : [
31522                 {
31523                     tag: "rect",
31524                     id: this.svgID + "-svg-r",
31525                     width: this.width,
31526                     height: this.height,
31527                     fill: "#ffa"
31528                 },
31529                 {
31530                     tag: "line",
31531                     id: this.svgID + "-svg-l",
31532                     x1: "0", // start
31533                     y1: (this.height*0.8), // start set the line in 80% of height
31534                     x2: this.width, // end
31535                     y2: (this.height*0.8), // end set the line in 80% of height
31536                     'stroke': "#666",
31537                     'stroke-width': "1",
31538                     'stroke-dasharray': "3",
31539                     'shape-rendering': "crispEdges",
31540                     'pointer-events': "none"
31541                 },
31542                 {
31543                     tag: "path",
31544                     id: this.svgID + "-svg-p",
31545                     'stroke': "navy",
31546                     'stroke-width': "3",
31547                     'fill': "none",
31548                     'pointer-events': 'none'
31549                 }
31550               ]
31551         });
31552         this.createSVG();
31553         this.svgBox = this.svgEl.dom.getScreenCTM();
31554     },
31555     createSVG : function(){ 
31556         var svg = this.signPanel;
31557         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
31558         var t = this;
31559
31560         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
31561         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
31562         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
31563         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
31564         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
31565         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
31566         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
31567         
31568     },
31569     isTouchEvent : function(e){
31570         return e.type.match(/^touch/);
31571     },
31572     getCoords : function (e) {
31573         var pt    = this.svgEl.dom.createSVGPoint();
31574         pt.x = e.clientX; 
31575         pt.y = e.clientY;
31576         if (this.isTouchEvent(e)) {
31577             pt.x =  e.targetTouches[0].clientX;
31578             pt.y = e.targetTouches[0].clientY;
31579         }
31580         var a = this.svgEl.dom.getScreenCTM();
31581         var b = a.inverse();
31582         var mx = pt.matrixTransform(b);
31583         return mx.x + ',' + mx.y;
31584     },
31585     //mouse event headler 
31586     down : function (e) {
31587         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
31588         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
31589         
31590         this.isMouseDown = true;
31591         
31592         e.preventDefault();
31593     },
31594     move : function (e) {
31595         if (this.isMouseDown) {
31596             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
31597             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
31598         }
31599         
31600         e.preventDefault();
31601     },
31602     up : function (e) {
31603         this.isMouseDown = false;
31604         var sp = this.signatureTmp.split(' ');
31605         
31606         if(sp.length > 1){
31607             if(!sp[sp.length-2].match(/^L/)){
31608                 sp.pop();
31609                 sp.pop();
31610                 sp.push("");
31611                 this.signatureTmp = sp.join(" ");
31612             }
31613         }
31614         if(this.getValue() != this.signatureTmp){
31615             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31616             this.isConfirmed = false;
31617         }
31618         e.preventDefault();
31619     },
31620     
31621     /**
31622      * Protected method that will not generally be called directly. It
31623      * is called when the editor creates its toolbar. Override this method if you need to
31624      * add custom toolbar buttons.
31625      * @param {HtmlEditor} editor
31626      */
31627     createToolbar : function(editor){
31628          function btn(id, toggle, handler){
31629             var xid = fid + '-'+ id ;
31630             return {
31631                 id : xid,
31632                 cmd : id,
31633                 cls : 'x-btn-icon x-edit-'+id,
31634                 enableToggle:toggle !== false,
31635                 scope: editor, // was editor...
31636                 handler:handler||editor.relayBtnCmd,
31637                 clickEvent:'mousedown',
31638                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
31639                 tabIndex:-1
31640             };
31641         }
31642         
31643         
31644         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
31645         this.tb = tb;
31646         this.tb.add(
31647            {
31648                 cls : ' x-signature-btn x-signature-'+id,
31649                 scope: editor, // was editor...
31650                 handler: this.reset,
31651                 clickEvent:'mousedown',
31652                 text: this.labels.clear
31653             },
31654             {
31655                  xtype : 'Fill',
31656                  xns: Roo.Toolbar
31657             }, 
31658             {
31659                 cls : '  x-signature-btn x-signature-'+id,
31660                 scope: editor, // was editor...
31661                 handler: this.confirmHandler,
31662                 clickEvent:'mousedown',
31663                 text: this.labels.confirm
31664             }
31665         );
31666     
31667     },
31668     //public
31669     /**
31670      * when user is clicked confirm then show this image.....
31671      * 
31672      * @return {String} Image Data URI
31673      */
31674     getImageDataURI : function(){
31675         var svg = this.svgEl.dom.parentNode.innerHTML;
31676         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
31677         return src; 
31678     },
31679     /**
31680      * 
31681      * @return {Boolean} this.isConfirmed
31682      */
31683     getConfirmed : function(){
31684         return this.isConfirmed;
31685     },
31686     /**
31687      * 
31688      * @return {Number} this.width
31689      */
31690     getWidth : function(){
31691         return this.width;
31692     },
31693     /**
31694      * 
31695      * @return {Number} this.height
31696      */
31697     getHeight : function(){
31698         return this.height;
31699     },
31700     // private
31701     getSignature : function(){
31702         return this.signatureTmp;
31703     },
31704     // private
31705     reset : function(){
31706         this.signatureTmp = '';
31707         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31708         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
31709         this.isConfirmed = false;
31710         Roo.form.Signature.superclass.reset.call(this);
31711     },
31712     setSignature : function(s){
31713         this.signatureTmp = s;
31714         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31715         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
31716         this.setValue(s);
31717         this.isConfirmed = false;
31718         Roo.form.Signature.superclass.reset.call(this);
31719     }, 
31720     test : function(){
31721 //        Roo.log(this.signPanel.dom.contentWindow.up())
31722     },
31723     //private
31724     setConfirmed : function(){
31725         
31726         
31727         
31728 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
31729     },
31730     // private
31731     confirmHandler : function(){
31732         if(!this.getSignature()){
31733             return;
31734         }
31735         
31736         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
31737         this.setValue(this.getSignature());
31738         this.isConfirmed = true;
31739         
31740         this.fireEvent('confirm', this);
31741     },
31742     // private
31743     // Subclasses should provide the validation implementation by overriding this
31744     validateValue : function(value){
31745         if(this.allowBlank){
31746             return true;
31747         }
31748         
31749         if(this.isConfirmed){
31750             return true;
31751         }
31752         return false;
31753     }
31754 });/*
31755  * Based on:
31756  * Ext JS Library 1.1.1
31757  * Copyright(c) 2006-2007, Ext JS, LLC.
31758  *
31759  * Originally Released Under LGPL - original licence link has changed is not relivant.
31760  *
31761  * Fork - LGPL
31762  * <script type="text/javascript">
31763  */
31764  
31765
31766 /**
31767  * @class Roo.form.ComboBox
31768  * @extends Roo.form.TriggerField
31769  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
31770  * @constructor
31771  * Create a new ComboBox.
31772  * @param {Object} config Configuration options
31773  */
31774 Roo.form.Select = function(config){
31775     Roo.form.Select.superclass.constructor.call(this, config);
31776      
31777 };
31778
31779 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
31780     /**
31781      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
31782      */
31783     /**
31784      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
31785      * rendering into an Roo.Editor, defaults to false)
31786      */
31787     /**
31788      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
31789      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
31790      */
31791     /**
31792      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
31793      */
31794     /**
31795      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
31796      * the dropdown list (defaults to undefined, with no header element)
31797      */
31798
31799      /**
31800      * @cfg {String/Roo.Template} tpl The template to use to render the output
31801      */
31802      
31803     // private
31804     defaultAutoCreate : {tag: "select"  },
31805     /**
31806      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
31807      */
31808     listWidth: undefined,
31809     /**
31810      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
31811      * mode = 'remote' or 'text' if mode = 'local')
31812      */
31813     displayField: undefined,
31814     /**
31815      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
31816      * mode = 'remote' or 'value' if mode = 'local'). 
31817      * Note: use of a valueField requires the user make a selection
31818      * in order for a value to be mapped.
31819      */
31820     valueField: undefined,
31821     
31822     
31823     /**
31824      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
31825      * field's data value (defaults to the underlying DOM element's name)
31826      */
31827     hiddenName: undefined,
31828     /**
31829      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
31830      */
31831     listClass: '',
31832     /**
31833      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
31834      */
31835     selectedClass: 'x-combo-selected',
31836     /**
31837      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
31838      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
31839      * which displays a downward arrow icon).
31840      */
31841     triggerClass : 'x-form-arrow-trigger',
31842     /**
31843      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31844      */
31845     shadow:'sides',
31846     /**
31847      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
31848      * anchor positions (defaults to 'tl-bl')
31849      */
31850     listAlign: 'tl-bl?',
31851     /**
31852      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
31853      */
31854     maxHeight: 300,
31855     /**
31856      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
31857      * query specified by the allQuery config option (defaults to 'query')
31858      */
31859     triggerAction: 'query',
31860     /**
31861      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
31862      * (defaults to 4, does not apply if editable = false)
31863      */
31864     minChars : 4,
31865     /**
31866      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
31867      * delay (typeAheadDelay) if it matches a known value (defaults to false)
31868      */
31869     typeAhead: false,
31870     /**
31871      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
31872      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
31873      */
31874     queryDelay: 500,
31875     /**
31876      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
31877      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
31878      */
31879     pageSize: 0,
31880     /**
31881      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
31882      * when editable = true (defaults to false)
31883      */
31884     selectOnFocus:false,
31885     /**
31886      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
31887      */
31888     queryParam: 'query',
31889     /**
31890      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
31891      * when mode = 'remote' (defaults to 'Loading...')
31892      */
31893     loadingText: 'Loading...',
31894     /**
31895      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
31896      */
31897     resizable: false,
31898     /**
31899      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
31900      */
31901     handleHeight : 8,
31902     /**
31903      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
31904      * traditional select (defaults to true)
31905      */
31906     editable: true,
31907     /**
31908      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
31909      */
31910     allQuery: '',
31911     /**
31912      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
31913      */
31914     mode: 'remote',
31915     /**
31916      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
31917      * listWidth has a higher value)
31918      */
31919     minListWidth : 70,
31920     /**
31921      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
31922      * allow the user to set arbitrary text into the field (defaults to false)
31923      */
31924     forceSelection:false,
31925     /**
31926      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
31927      * if typeAhead = true (defaults to 250)
31928      */
31929     typeAheadDelay : 250,
31930     /**
31931      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
31932      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
31933      */
31934     valueNotFoundText : undefined,
31935     
31936     /**
31937      * @cfg {String} defaultValue The value displayed after loading the store.
31938      */
31939     defaultValue: '',
31940     
31941     /**
31942      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
31943      */
31944     blockFocus : false,
31945     
31946     /**
31947      * @cfg {Boolean} disableClear Disable showing of clear button.
31948      */
31949     disableClear : false,
31950     /**
31951      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
31952      */
31953     alwaysQuery : false,
31954     
31955     //private
31956     addicon : false,
31957     editicon: false,
31958     
31959     // element that contains real text value.. (when hidden is used..)
31960      
31961     // private
31962     onRender : function(ct, position){
31963         Roo.form.Field.prototype.onRender.call(this, ct, position);
31964         
31965         if(this.store){
31966             this.store.on('beforeload', this.onBeforeLoad, this);
31967             this.store.on('load', this.onLoad, this);
31968             this.store.on('loadexception', this.onLoadException, this);
31969             this.store.load({});
31970         }
31971         
31972         
31973         
31974     },
31975
31976     // private
31977     initEvents : function(){
31978         //Roo.form.ComboBox.superclass.initEvents.call(this);
31979  
31980     },
31981
31982     onDestroy : function(){
31983        
31984         if(this.store){
31985             this.store.un('beforeload', this.onBeforeLoad, this);
31986             this.store.un('load', this.onLoad, this);
31987             this.store.un('loadexception', this.onLoadException, this);
31988         }
31989         //Roo.form.ComboBox.superclass.onDestroy.call(this);
31990     },
31991
31992     // private
31993     fireKey : function(e){
31994         if(e.isNavKeyPress() && !this.list.isVisible()){
31995             this.fireEvent("specialkey", this, e);
31996         }
31997     },
31998
31999     // private
32000     onResize: function(w, h){
32001         
32002         return; 
32003     
32004         
32005     },
32006
32007     /**
32008      * Allow or prevent the user from directly editing the field text.  If false is passed,
32009      * the user will only be able to select from the items defined in the dropdown list.  This method
32010      * is the runtime equivalent of setting the 'editable' config option at config time.
32011      * @param {Boolean} value True to allow the user to directly edit the field text
32012      */
32013     setEditable : function(value){
32014          
32015     },
32016
32017     // private
32018     onBeforeLoad : function(){
32019         
32020         Roo.log("Select before load");
32021         return;
32022     
32023         this.innerList.update(this.loadingText ?
32024                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
32025         //this.restrictHeight();
32026         this.selectedIndex = -1;
32027     },
32028
32029     // private
32030     onLoad : function(){
32031
32032     
32033         var dom = this.el.dom;
32034         dom.innerHTML = '';
32035          var od = dom.ownerDocument;
32036          
32037         if (this.emptyText) {
32038             var op = od.createElement('option');
32039             op.setAttribute('value', '');
32040             op.innerHTML = String.format('{0}', this.emptyText);
32041             dom.appendChild(op);
32042         }
32043         if(this.store.getCount() > 0){
32044            
32045             var vf = this.valueField;
32046             var df = this.displayField;
32047             this.store.data.each(function(r) {
32048                 // which colmsn to use... testing - cdoe / title..
32049                 var op = od.createElement('option');
32050                 op.setAttribute('value', r.data[vf]);
32051                 op.innerHTML = String.format('{0}', r.data[df]);
32052                 dom.appendChild(op);
32053             });
32054             if (typeof(this.defaultValue != 'undefined')) {
32055                 this.setValue(this.defaultValue);
32056             }
32057             
32058              
32059         }else{
32060             //this.onEmptyResults();
32061         }
32062         //this.el.focus();
32063     },
32064     // private
32065     onLoadException : function()
32066     {
32067         dom.innerHTML = '';
32068             
32069         Roo.log("Select on load exception");
32070         return;
32071     
32072         this.collapse();
32073         Roo.log(this.store.reader.jsonData);
32074         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
32075             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
32076         }
32077         
32078         
32079     },
32080     // private
32081     onTypeAhead : function(){
32082          
32083     },
32084
32085     // private
32086     onSelect : function(record, index){
32087         Roo.log('on select?');
32088         return;
32089         if(this.fireEvent('beforeselect', this, record, index) !== false){
32090             this.setFromData(index > -1 ? record.data : false);
32091             this.collapse();
32092             this.fireEvent('select', this, record, index);
32093         }
32094     },
32095
32096     /**
32097      * Returns the currently selected field value or empty string if no value is set.
32098      * @return {String} value The selected value
32099      */
32100     getValue : function(){
32101         var dom = this.el.dom;
32102         this.value = dom.options[dom.selectedIndex].value;
32103         return this.value;
32104         
32105     },
32106
32107     /**
32108      * Clears any text/value currently set in the field
32109      */
32110     clearValue : function(){
32111         this.value = '';
32112         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
32113         
32114     },
32115
32116     /**
32117      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
32118      * will be displayed in the field.  If the value does not match the data value of an existing item,
32119      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
32120      * Otherwise the field will be blank (although the value will still be set).
32121      * @param {String} value The value to match
32122      */
32123     setValue : function(v){
32124         var d = this.el.dom;
32125         for (var i =0; i < d.options.length;i++) {
32126             if (v == d.options[i].value) {
32127                 d.selectedIndex = i;
32128                 this.value = v;
32129                 return;
32130             }
32131         }
32132         this.clearValue();
32133     },
32134     /**
32135      * @property {Object} the last set data for the element
32136      */
32137     
32138     lastData : false,
32139     /**
32140      * Sets the value of the field based on a object which is related to the record format for the store.
32141      * @param {Object} value the value to set as. or false on reset?
32142      */
32143     setFromData : function(o){
32144         Roo.log('setfrom data?');
32145          
32146         
32147         
32148     },
32149     // private
32150     reset : function(){
32151         this.clearValue();
32152     },
32153     // private
32154     findRecord : function(prop, value){
32155         
32156         return false;
32157     
32158         var record;
32159         if(this.store.getCount() > 0){
32160             this.store.each(function(r){
32161                 if(r.data[prop] == value){
32162                     record = r;
32163                     return false;
32164                 }
32165                 return true;
32166             });
32167         }
32168         return record;
32169     },
32170     
32171     getName: function()
32172     {
32173         // returns hidden if it's set..
32174         if (!this.rendered) {return ''};
32175         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
32176         
32177     },
32178      
32179
32180     
32181
32182     // private
32183     onEmptyResults : function(){
32184         Roo.log('empty results');
32185         //this.collapse();
32186     },
32187
32188     /**
32189      * Returns true if the dropdown list is expanded, else false.
32190      */
32191     isExpanded : function(){
32192         return false;
32193     },
32194
32195     /**
32196      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
32197      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32198      * @param {String} value The data value of the item to select
32199      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32200      * selected item if it is not currently in view (defaults to true)
32201      * @return {Boolean} True if the value matched an item in the list, else false
32202      */
32203     selectByValue : function(v, scrollIntoView){
32204         Roo.log('select By Value');
32205         return false;
32206     
32207         if(v !== undefined && v !== null){
32208             var r = this.findRecord(this.valueField || this.displayField, v);
32209             if(r){
32210                 this.select(this.store.indexOf(r), scrollIntoView);
32211                 return true;
32212             }
32213         }
32214         return false;
32215     },
32216
32217     /**
32218      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
32219      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32220      * @param {Number} index The zero-based index of the list item to select
32221      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32222      * selected item if it is not currently in view (defaults to true)
32223      */
32224     select : function(index, scrollIntoView){
32225         Roo.log('select ');
32226         return  ;
32227         
32228         this.selectedIndex = index;
32229         this.view.select(index);
32230         if(scrollIntoView !== false){
32231             var el = this.view.getNode(index);
32232             if(el){
32233                 this.innerList.scrollChildIntoView(el, false);
32234             }
32235         }
32236     },
32237
32238       
32239
32240     // private
32241     validateBlur : function(){
32242         
32243         return;
32244         
32245     },
32246
32247     // private
32248     initQuery : function(){
32249         this.doQuery(this.getRawValue());
32250     },
32251
32252     // private
32253     doForce : function(){
32254         if(this.el.dom.value.length > 0){
32255             this.el.dom.value =
32256                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
32257              
32258         }
32259     },
32260
32261     /**
32262      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
32263      * query allowing the query action to be canceled if needed.
32264      * @param {String} query The SQL query to execute
32265      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
32266      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
32267      * saved in the current store (defaults to false)
32268      */
32269     doQuery : function(q, forceAll){
32270         
32271         Roo.log('doQuery?');
32272         if(q === undefined || q === null){
32273             q = '';
32274         }
32275         var qe = {
32276             query: q,
32277             forceAll: forceAll,
32278             combo: this,
32279             cancel:false
32280         };
32281         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
32282             return false;
32283         }
32284         q = qe.query;
32285         forceAll = qe.forceAll;
32286         if(forceAll === true || (q.length >= this.minChars)){
32287             if(this.lastQuery != q || this.alwaysQuery){
32288                 this.lastQuery = q;
32289                 if(this.mode == 'local'){
32290                     this.selectedIndex = -1;
32291                     if(forceAll){
32292                         this.store.clearFilter();
32293                     }else{
32294                         this.store.filter(this.displayField, q);
32295                     }
32296                     this.onLoad();
32297                 }else{
32298                     this.store.baseParams[this.queryParam] = q;
32299                     this.store.load({
32300                         params: this.getParams(q)
32301                     });
32302                     this.expand();
32303                 }
32304             }else{
32305                 this.selectedIndex = -1;
32306                 this.onLoad();   
32307             }
32308         }
32309     },
32310
32311     // private
32312     getParams : function(q){
32313         var p = {};
32314         //p[this.queryParam] = q;
32315         if(this.pageSize){
32316             p.start = 0;
32317             p.limit = this.pageSize;
32318         }
32319         return p;
32320     },
32321
32322     /**
32323      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
32324      */
32325     collapse : function(){
32326         
32327     },
32328
32329     // private
32330     collapseIf : function(e){
32331         
32332     },
32333
32334     /**
32335      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
32336      */
32337     expand : function(){
32338         
32339     } ,
32340
32341     // private
32342      
32343
32344     /** 
32345     * @cfg {Boolean} grow 
32346     * @hide 
32347     */
32348     /** 
32349     * @cfg {Number} growMin 
32350     * @hide 
32351     */
32352     /** 
32353     * @cfg {Number} growMax 
32354     * @hide 
32355     */
32356     /**
32357      * @hide
32358      * @method autoSize
32359      */
32360     
32361     setWidth : function()
32362     {
32363         
32364     },
32365     getResizeEl : function(){
32366         return this.el;
32367     }
32368 });//<script type="text/javasscript">
32369  
32370
32371 /**
32372  * @class Roo.DDView
32373  * A DnD enabled version of Roo.View.
32374  * @param {Element/String} container The Element in which to create the View.
32375  * @param {String} tpl The template string used to create the markup for each element of the View
32376  * @param {Object} config The configuration properties. These include all the config options of
32377  * {@link Roo.View} plus some specific to this class.<br>
32378  * <p>
32379  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
32380  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
32381  * <p>
32382  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
32383 .x-view-drag-insert-above {
32384         border-top:1px dotted #3366cc;
32385 }
32386 .x-view-drag-insert-below {
32387         border-bottom:1px dotted #3366cc;
32388 }
32389 </code></pre>
32390  * 
32391  */
32392  
32393 Roo.DDView = function(container, tpl, config) {
32394     Roo.DDView.superclass.constructor.apply(this, arguments);
32395     this.getEl().setStyle("outline", "0px none");
32396     this.getEl().unselectable();
32397     if (this.dragGroup) {
32398                 this.setDraggable(this.dragGroup.split(","));
32399     }
32400     if (this.dropGroup) {
32401                 this.setDroppable(this.dropGroup.split(","));
32402     }
32403     if (this.deletable) {
32404         this.setDeletable();
32405     }
32406     this.isDirtyFlag = false;
32407         this.addEvents({
32408                 "drop" : true
32409         });
32410 };
32411
32412 Roo.extend(Roo.DDView, Roo.View, {
32413 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
32414 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
32415 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
32416 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
32417
32418         isFormField: true,
32419
32420         reset: Roo.emptyFn,
32421         
32422         clearInvalid: Roo.form.Field.prototype.clearInvalid,
32423
32424         validate: function() {
32425                 return true;
32426         },
32427         
32428         destroy: function() {
32429                 this.purgeListeners();
32430                 this.getEl.removeAllListeners();
32431                 this.getEl().remove();
32432                 if (this.dragZone) {
32433                         if (this.dragZone.destroy) {
32434                                 this.dragZone.destroy();
32435                         }
32436                 }
32437                 if (this.dropZone) {
32438                         if (this.dropZone.destroy) {
32439                                 this.dropZone.destroy();
32440                         }
32441                 }
32442         },
32443
32444 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
32445         getName: function() {
32446                 return this.name;
32447         },
32448
32449 /**     Loads the View from a JSON string representing the Records to put into the Store. */
32450         setValue: function(v) {
32451                 if (!this.store) {
32452                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
32453                 }
32454                 var data = {};
32455                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
32456                 this.store.proxy = new Roo.data.MemoryProxy(data);
32457                 this.store.load();
32458         },
32459
32460 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
32461         getValue: function() {
32462                 var result = '(';
32463                 this.store.each(function(rec) {
32464                         result += rec.id + ',';
32465                 });
32466                 return result.substr(0, result.length - 1) + ')';
32467         },
32468         
32469         getIds: function() {
32470                 var i = 0, result = new Array(this.store.getCount());
32471                 this.store.each(function(rec) {
32472                         result[i++] = rec.id;
32473                 });
32474                 return result;
32475         },
32476         
32477         isDirty: function() {
32478                 return this.isDirtyFlag;
32479         },
32480
32481 /**
32482  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
32483  *      whole Element becomes the target, and this causes the drop gesture to append.
32484  */
32485     getTargetFromEvent : function(e) {
32486                 var target = e.getTarget();
32487                 while ((target !== null) && (target.parentNode != this.el.dom)) {
32488                 target = target.parentNode;
32489                 }
32490                 if (!target) {
32491                         target = this.el.dom.lastChild || this.el.dom;
32492                 }
32493                 return target;
32494     },
32495
32496 /**
32497  *      Create the drag data which consists of an object which has the property "ddel" as
32498  *      the drag proxy element. 
32499  */
32500     getDragData : function(e) {
32501         var target = this.findItemFromChild(e.getTarget());
32502                 if(target) {
32503                         this.handleSelection(e);
32504                         var selNodes = this.getSelectedNodes();
32505             var dragData = {
32506                 source: this,
32507                 copy: this.copy || (this.allowCopy && e.ctrlKey),
32508                 nodes: selNodes,
32509                 records: []
32510                         };
32511                         var selectedIndices = this.getSelectedIndexes();
32512                         for (var i = 0; i < selectedIndices.length; i++) {
32513                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
32514                         }
32515                         if (selNodes.length == 1) {
32516                                 dragData.ddel = target.cloneNode(true); // the div element
32517                         } else {
32518                                 var div = document.createElement('div'); // create the multi element drag "ghost"
32519                                 div.className = 'multi-proxy';
32520                                 for (var i = 0, len = selNodes.length; i < len; i++) {
32521                                         div.appendChild(selNodes[i].cloneNode(true));
32522                                 }
32523                                 dragData.ddel = div;
32524                         }
32525             //console.log(dragData)
32526             //console.log(dragData.ddel.innerHTML)
32527                         return dragData;
32528                 }
32529         //console.log('nodragData')
32530                 return false;
32531     },
32532     
32533 /**     Specify to which ddGroup items in this DDView may be dragged. */
32534     setDraggable: function(ddGroup) {
32535         if (ddGroup instanceof Array) {
32536                 Roo.each(ddGroup, this.setDraggable, this);
32537                 return;
32538         }
32539         if (this.dragZone) {
32540                 this.dragZone.addToGroup(ddGroup);
32541         } else {
32542                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
32543                                 containerScroll: true,
32544                                 ddGroup: ddGroup 
32545
32546                         });
32547 //                      Draggability implies selection. DragZone's mousedown selects the element.
32548                         if (!this.multiSelect) { this.singleSelect = true; }
32549
32550 //                      Wire the DragZone's handlers up to methods in *this*
32551                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
32552                 }
32553     },
32554
32555 /**     Specify from which ddGroup this DDView accepts drops. */
32556     setDroppable: function(ddGroup) {
32557         if (ddGroup instanceof Array) {
32558                 Roo.each(ddGroup, this.setDroppable, this);
32559                 return;
32560         }
32561         if (this.dropZone) {
32562                 this.dropZone.addToGroup(ddGroup);
32563         } else {
32564                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
32565                                 containerScroll: true,
32566                                 ddGroup: ddGroup
32567                         });
32568
32569 //                      Wire the DropZone's handlers up to methods in *this*
32570                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
32571                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
32572                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
32573                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
32574                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
32575                 }
32576     },
32577
32578 /**     Decide whether to drop above or below a View node. */
32579     getDropPoint : function(e, n, dd){
32580         if (n == this.el.dom) { return "above"; }
32581                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
32582                 var c = t + (b - t) / 2;
32583                 var y = Roo.lib.Event.getPageY(e);
32584                 if(y <= c) {
32585                         return "above";
32586                 }else{
32587                         return "below";
32588                 }
32589     },
32590
32591     onNodeEnter : function(n, dd, e, data){
32592                 return false;
32593     },
32594     
32595     onNodeOver : function(n, dd, e, data){
32596                 var pt = this.getDropPoint(e, n, dd);
32597                 // set the insert point style on the target node
32598                 var dragElClass = this.dropNotAllowed;
32599                 if (pt) {
32600                         var targetElClass;
32601                         if (pt == "above"){
32602                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
32603                                 targetElClass = "x-view-drag-insert-above";
32604                         } else {
32605                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
32606                                 targetElClass = "x-view-drag-insert-below";
32607                         }
32608                         if (this.lastInsertClass != targetElClass){
32609                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
32610                                 this.lastInsertClass = targetElClass;
32611                         }
32612                 }
32613                 return dragElClass;
32614         },
32615
32616     onNodeOut : function(n, dd, e, data){
32617                 this.removeDropIndicators(n);
32618     },
32619
32620     onNodeDrop : function(n, dd, e, data){
32621         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
32622                 return false;
32623         }
32624         var pt = this.getDropPoint(e, n, dd);
32625                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
32626                 if (pt == "below") { insertAt++; }
32627                 for (var i = 0; i < data.records.length; i++) {
32628                         var r = data.records[i];
32629                         var dup = this.store.getById(r.id);
32630                         if (dup && (dd != this.dragZone)) {
32631                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
32632                         } else {
32633                                 if (data.copy) {
32634                                         this.store.insert(insertAt++, r.copy());
32635                                 } else {
32636                                         data.source.isDirtyFlag = true;
32637                                         r.store.remove(r);
32638                                         this.store.insert(insertAt++, r);
32639                                 }
32640                                 this.isDirtyFlag = true;
32641                         }
32642                 }
32643                 this.dragZone.cachedTarget = null;
32644                 return true;
32645     },
32646
32647     removeDropIndicators : function(n){
32648                 if(n){
32649                         Roo.fly(n).removeClass([
32650                                 "x-view-drag-insert-above",
32651                                 "x-view-drag-insert-below"]);
32652                         this.lastInsertClass = "_noclass";
32653                 }
32654     },
32655
32656 /**
32657  *      Utility method. Add a delete option to the DDView's context menu.
32658  *      @param {String} imageUrl The URL of the "delete" icon image.
32659  */
32660         setDeletable: function(imageUrl) {
32661                 if (!this.singleSelect && !this.multiSelect) {
32662                         this.singleSelect = true;
32663                 }
32664                 var c = this.getContextMenu();
32665                 this.contextMenu.on("itemclick", function(item) {
32666                         switch (item.id) {
32667                                 case "delete":
32668                                         this.remove(this.getSelectedIndexes());
32669                                         break;
32670                         }
32671                 }, this);
32672                 this.contextMenu.add({
32673                         icon: imageUrl,
32674                         id: "delete",
32675                         text: 'Delete'
32676                 });
32677         },
32678         
32679 /**     Return the context menu for this DDView. */
32680         getContextMenu: function() {
32681                 if (!this.contextMenu) {
32682 //                      Create the View's context menu
32683                         this.contextMenu = new Roo.menu.Menu({
32684                                 id: this.id + "-contextmenu"
32685                         });
32686                         this.el.on("contextmenu", this.showContextMenu, this);
32687                 }
32688                 return this.contextMenu;
32689         },
32690         
32691         disableContextMenu: function() {
32692                 if (this.contextMenu) {
32693                         this.el.un("contextmenu", this.showContextMenu, this);
32694                 }
32695         },
32696
32697         showContextMenu: function(e, item) {
32698         item = this.findItemFromChild(e.getTarget());
32699                 if (item) {
32700                         e.stopEvent();
32701                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
32702                         this.contextMenu.showAt(e.getXY());
32703             }
32704     },
32705
32706 /**
32707  *      Remove {@link Roo.data.Record}s at the specified indices.
32708  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
32709  */
32710     remove: function(selectedIndices) {
32711                 selectedIndices = [].concat(selectedIndices);
32712                 for (var i = 0; i < selectedIndices.length; i++) {
32713                         var rec = this.store.getAt(selectedIndices[i]);
32714                         this.store.remove(rec);
32715                 }
32716     },
32717
32718 /**
32719  *      Double click fires the event, but also, if this is draggable, and there is only one other
32720  *      related DropZone, it transfers the selected node.
32721  */
32722     onDblClick : function(e){
32723         var item = this.findItemFromChild(e.getTarget());
32724         if(item){
32725             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
32726                 return false;
32727             }
32728             if (this.dragGroup) {
32729                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
32730                     while (targets.indexOf(this.dropZone) > -1) {
32731                             targets.remove(this.dropZone);
32732                                 }
32733                     if (targets.length == 1) {
32734                                         this.dragZone.cachedTarget = null;
32735                         var el = Roo.get(targets[0].getEl());
32736                         var box = el.getBox(true);
32737                         targets[0].onNodeDrop(el.dom, {
32738                                 target: el.dom,
32739                                 xy: [box.x, box.y + box.height - 1]
32740                         }, null, this.getDragData(e));
32741                     }
32742                 }
32743         }
32744     },
32745     
32746     handleSelection: function(e) {
32747                 this.dragZone.cachedTarget = null;
32748         var item = this.findItemFromChild(e.getTarget());
32749         if (!item) {
32750                 this.clearSelections(true);
32751                 return;
32752         }
32753                 if (item && (this.multiSelect || this.singleSelect)){
32754                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
32755                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
32756                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
32757                                 this.unselect(item);
32758                         } else {
32759                                 this.select(item, this.multiSelect && e.ctrlKey);
32760                                 this.lastSelection = item;
32761                         }
32762                 }
32763     },
32764
32765     onItemClick : function(item, index, e){
32766                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
32767                         return false;
32768                 }
32769                 return true;
32770     },
32771
32772     unselect : function(nodeInfo, suppressEvent){
32773                 var node = this.getNode(nodeInfo);
32774                 if(node && this.isSelected(node)){
32775                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
32776                                 Roo.fly(node).removeClass(this.selectedClass);
32777                                 this.selections.remove(node);
32778                                 if(!suppressEvent){
32779                                         this.fireEvent("selectionchange", this, this.selections);
32780                                 }
32781                         }
32782                 }
32783     }
32784 });
32785 /*
32786  * Based on:
32787  * Ext JS Library 1.1.1
32788  * Copyright(c) 2006-2007, Ext JS, LLC.
32789  *
32790  * Originally Released Under LGPL - original licence link has changed is not relivant.
32791  *
32792  * Fork - LGPL
32793  * <script type="text/javascript">
32794  */
32795  
32796 /**
32797  * @class Roo.LayoutManager
32798  * @extends Roo.util.Observable
32799  * Base class for layout managers.
32800  */
32801 Roo.LayoutManager = function(container, config){
32802     Roo.LayoutManager.superclass.constructor.call(this);
32803     this.el = Roo.get(container);
32804     // ie scrollbar fix
32805     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32806         document.body.scroll = "no";
32807     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32808         this.el.position('relative');
32809     }
32810     this.id = this.el.id;
32811     this.el.addClass("x-layout-container");
32812     /** false to disable window resize monitoring @type Boolean */
32813     this.monitorWindowResize = true;
32814     this.regions = {};
32815     this.addEvents({
32816         /**
32817          * @event layout
32818          * Fires when a layout is performed. 
32819          * @param {Roo.LayoutManager} this
32820          */
32821         "layout" : true,
32822         /**
32823          * @event regionresized
32824          * Fires when the user resizes a region. 
32825          * @param {Roo.LayoutRegion} region The resized region
32826          * @param {Number} newSize The new size (width for east/west, height for north/south)
32827          */
32828         "regionresized" : true,
32829         /**
32830          * @event regioncollapsed
32831          * Fires when a region is collapsed. 
32832          * @param {Roo.LayoutRegion} region The collapsed region
32833          */
32834         "regioncollapsed" : true,
32835         /**
32836          * @event regionexpanded
32837          * Fires when a region is expanded.  
32838          * @param {Roo.LayoutRegion} region The expanded region
32839          */
32840         "regionexpanded" : true
32841     });
32842     this.updating = false;
32843     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32844 };
32845
32846 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
32847     /**
32848      * Returns true if this layout is currently being updated
32849      * @return {Boolean}
32850      */
32851     isUpdating : function(){
32852         return this.updating; 
32853     },
32854     
32855     /**
32856      * Suspend the LayoutManager from doing auto-layouts while
32857      * making multiple add or remove calls
32858      */
32859     beginUpdate : function(){
32860         this.updating = true;    
32861     },
32862     
32863     /**
32864      * Restore auto-layouts and optionally disable the manager from performing a layout
32865      * @param {Boolean} noLayout true to disable a layout update 
32866      */
32867     endUpdate : function(noLayout){
32868         this.updating = false;
32869         if(!noLayout){
32870             this.layout();
32871         }    
32872     },
32873     
32874     layout: function(){
32875         
32876     },
32877     
32878     onRegionResized : function(region, newSize){
32879         this.fireEvent("regionresized", region, newSize);
32880         this.layout();
32881     },
32882     
32883     onRegionCollapsed : function(region){
32884         this.fireEvent("regioncollapsed", region);
32885     },
32886     
32887     onRegionExpanded : function(region){
32888         this.fireEvent("regionexpanded", region);
32889     },
32890         
32891     /**
32892      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32893      * performs box-model adjustments.
32894      * @return {Object} The size as an object {width: (the width), height: (the height)}
32895      */
32896     getViewSize : function(){
32897         var size;
32898         if(this.el.dom != document.body){
32899             size = this.el.getSize();
32900         }else{
32901             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32902         }
32903         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32904         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32905         return size;
32906     },
32907     
32908     /**
32909      * Returns the Element this layout is bound to.
32910      * @return {Roo.Element}
32911      */
32912     getEl : function(){
32913         return this.el;
32914     },
32915     
32916     /**
32917      * Returns the specified region.
32918      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32919      * @return {Roo.LayoutRegion}
32920      */
32921     getRegion : function(target){
32922         return this.regions[target.toLowerCase()];
32923     },
32924     
32925     onWindowResize : function(){
32926         if(this.monitorWindowResize){
32927             this.layout();
32928         }
32929     }
32930 });/*
32931  * Based on:
32932  * Ext JS Library 1.1.1
32933  * Copyright(c) 2006-2007, Ext JS, LLC.
32934  *
32935  * Originally Released Under LGPL - original licence link has changed is not relivant.
32936  *
32937  * Fork - LGPL
32938  * <script type="text/javascript">
32939  */
32940 /**
32941  * @class Roo.BorderLayout
32942  * @extends Roo.LayoutManager
32943  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32944  * please see: <br><br>
32945  * <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>
32946  * <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>
32947  * Example:
32948  <pre><code>
32949  var layout = new Roo.BorderLayout(document.body, {
32950     north: {
32951         initialSize: 25,
32952         titlebar: false
32953     },
32954     west: {
32955         split:true,
32956         initialSize: 200,
32957         minSize: 175,
32958         maxSize: 400,
32959         titlebar: true,
32960         collapsible: true
32961     },
32962     east: {
32963         split:true,
32964         initialSize: 202,
32965         minSize: 175,
32966         maxSize: 400,
32967         titlebar: true,
32968         collapsible: true
32969     },
32970     south: {
32971         split:true,
32972         initialSize: 100,
32973         minSize: 100,
32974         maxSize: 200,
32975         titlebar: true,
32976         collapsible: true
32977     },
32978     center: {
32979         titlebar: true,
32980         autoScroll:true,
32981         resizeTabs: true,
32982         minTabWidth: 50,
32983         preferredTabWidth: 150
32984     }
32985 });
32986
32987 // shorthand
32988 var CP = Roo.ContentPanel;
32989
32990 layout.beginUpdate();
32991 layout.add("north", new CP("north", "North"));
32992 layout.add("south", new CP("south", {title: "South", closable: true}));
32993 layout.add("west", new CP("west", {title: "West"}));
32994 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
32995 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
32996 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
32997 layout.getRegion("center").showPanel("center1");
32998 layout.endUpdate();
32999 </code></pre>
33000
33001 <b>The container the layout is rendered into can be either the body element or any other element.
33002 If it is not the body element, the container needs to either be an absolute positioned element,
33003 or you will need to add "position:relative" to the css of the container.  You will also need to specify
33004 the container size if it is not the body element.</b>
33005
33006 * @constructor
33007 * Create a new BorderLayout
33008 * @param {String/HTMLElement/Element} container The container this layout is bound to
33009 * @param {Object} config Configuration options
33010  */
33011 Roo.BorderLayout = function(container, config){
33012     config = config || {};
33013     Roo.BorderLayout.superclass.constructor.call(this, container, config);
33014     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
33015     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
33016         var target = this.factory.validRegions[i];
33017         if(config[target]){
33018             this.addRegion(target, config[target]);
33019         }
33020     }
33021 };
33022
33023 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
33024     /**
33025      * Creates and adds a new region if it doesn't already exist.
33026      * @param {String} target The target region key (north, south, east, west or center).
33027      * @param {Object} config The regions config object
33028      * @return {BorderLayoutRegion} The new region
33029      */
33030     addRegion : function(target, config){
33031         if(!this.regions[target]){
33032             var r = this.factory.create(target, this, config);
33033             this.bindRegion(target, r);
33034         }
33035         return this.regions[target];
33036     },
33037
33038     // private (kinda)
33039     bindRegion : function(name, r){
33040         this.regions[name] = r;
33041         r.on("visibilitychange", this.layout, this);
33042         r.on("paneladded", this.layout, this);
33043         r.on("panelremoved", this.layout, this);
33044         r.on("invalidated", this.layout, this);
33045         r.on("resized", this.onRegionResized, this);
33046         r.on("collapsed", this.onRegionCollapsed, this);
33047         r.on("expanded", this.onRegionExpanded, this);
33048     },
33049
33050     /**
33051      * Performs a layout update.
33052      */
33053     layout : function(){
33054         if(this.updating) {
33055             return;
33056         }
33057         var size = this.getViewSize();
33058         var w = size.width;
33059         var h = size.height;
33060         var centerW = w;
33061         var centerH = h;
33062         var centerY = 0;
33063         var centerX = 0;
33064         //var x = 0, y = 0;
33065
33066         var rs = this.regions;
33067         var north = rs["north"];
33068         var south = rs["south"]; 
33069         var west = rs["west"];
33070         var east = rs["east"];
33071         var center = rs["center"];
33072         //if(this.hideOnLayout){ // not supported anymore
33073             //c.el.setStyle("display", "none");
33074         //}
33075         if(north && north.isVisible()){
33076             var b = north.getBox();
33077             var m = north.getMargins();
33078             b.width = w - (m.left+m.right);
33079             b.x = m.left;
33080             b.y = m.top;
33081             centerY = b.height + b.y + m.bottom;
33082             centerH -= centerY;
33083             north.updateBox(this.safeBox(b));
33084         }
33085         if(south && south.isVisible()){
33086             var b = south.getBox();
33087             var m = south.getMargins();
33088             b.width = w - (m.left+m.right);
33089             b.x = m.left;
33090             var totalHeight = (b.height + m.top + m.bottom);
33091             b.y = h - totalHeight + m.top;
33092             centerH -= totalHeight;
33093             south.updateBox(this.safeBox(b));
33094         }
33095         if(west && west.isVisible()){
33096             var b = west.getBox();
33097             var m = west.getMargins();
33098             b.height = centerH - (m.top+m.bottom);
33099             b.x = m.left;
33100             b.y = centerY + m.top;
33101             var totalWidth = (b.width + m.left + m.right);
33102             centerX += totalWidth;
33103             centerW -= totalWidth;
33104             west.updateBox(this.safeBox(b));
33105         }
33106         if(east && east.isVisible()){
33107             var b = east.getBox();
33108             var m = east.getMargins();
33109             b.height = centerH - (m.top+m.bottom);
33110             var totalWidth = (b.width + m.left + m.right);
33111             b.x = w - totalWidth + m.left;
33112             b.y = centerY + m.top;
33113             centerW -= totalWidth;
33114             east.updateBox(this.safeBox(b));
33115         }
33116         if(center){
33117             var m = center.getMargins();
33118             var centerBox = {
33119                 x: centerX + m.left,
33120                 y: centerY + m.top,
33121                 width: centerW - (m.left+m.right),
33122                 height: centerH - (m.top+m.bottom)
33123             };
33124             //if(this.hideOnLayout){
33125                 //center.el.setStyle("display", "block");
33126             //}
33127             center.updateBox(this.safeBox(centerBox));
33128         }
33129         this.el.repaint();
33130         this.fireEvent("layout", this);
33131     },
33132
33133     // private
33134     safeBox : function(box){
33135         box.width = Math.max(0, box.width);
33136         box.height = Math.max(0, box.height);
33137         return box;
33138     },
33139
33140     /**
33141      * Adds a ContentPanel (or subclass) to this layout.
33142      * @param {String} target The target region key (north, south, east, west or center).
33143      * @param {Roo.ContentPanel} panel The panel to add
33144      * @return {Roo.ContentPanel} The added panel
33145      */
33146     add : function(target, panel){
33147          
33148         target = target.toLowerCase();
33149         return this.regions[target].add(panel);
33150     },
33151
33152     /**
33153      * Remove a ContentPanel (or subclass) to this layout.
33154      * @param {String} target The target region key (north, south, east, west or center).
33155      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
33156      * @return {Roo.ContentPanel} The removed panel
33157      */
33158     remove : function(target, panel){
33159         target = target.toLowerCase();
33160         return this.regions[target].remove(panel);
33161     },
33162
33163     /**
33164      * Searches all regions for a panel with the specified id
33165      * @param {String} panelId
33166      * @return {Roo.ContentPanel} The panel or null if it wasn't found
33167      */
33168     findPanel : function(panelId){
33169         var rs = this.regions;
33170         for(var target in rs){
33171             if(typeof rs[target] != "function"){
33172                 var p = rs[target].getPanel(panelId);
33173                 if(p){
33174                     return p;
33175                 }
33176             }
33177         }
33178         return null;
33179     },
33180
33181     /**
33182      * Searches all regions for a panel with the specified id and activates (shows) it.
33183      * @param {String/ContentPanel} panelId The panels id or the panel itself
33184      * @return {Roo.ContentPanel} The shown panel or null
33185      */
33186     showPanel : function(panelId) {
33187       var rs = this.regions;
33188       for(var target in rs){
33189          var r = rs[target];
33190          if(typeof r != "function"){
33191             if(r.hasPanel(panelId)){
33192                return r.showPanel(panelId);
33193             }
33194          }
33195       }
33196       return null;
33197    },
33198
33199    /**
33200      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
33201      * @param {Roo.state.Provider} provider (optional) An alternate state provider
33202      */
33203     restoreState : function(provider){
33204         if(!provider){
33205             provider = Roo.state.Manager;
33206         }
33207         var sm = new Roo.LayoutStateManager();
33208         sm.init(this, provider);
33209     },
33210
33211     /**
33212      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
33213      * object should contain properties for each region to add ContentPanels to, and each property's value should be
33214      * a valid ContentPanel config object.  Example:
33215      * <pre><code>
33216 // Create the main layout
33217 var layout = new Roo.BorderLayout('main-ct', {
33218     west: {
33219         split:true,
33220         minSize: 175,
33221         titlebar: true
33222     },
33223     center: {
33224         title:'Components'
33225     }
33226 }, 'main-ct');
33227
33228 // Create and add multiple ContentPanels at once via configs
33229 layout.batchAdd({
33230    west: {
33231        id: 'source-files',
33232        autoCreate:true,
33233        title:'Ext Source Files',
33234        autoScroll:true,
33235        fitToFrame:true
33236    },
33237    center : {
33238        el: cview,
33239        autoScroll:true,
33240        fitToFrame:true,
33241        toolbar: tb,
33242        resizeEl:'cbody'
33243    }
33244 });
33245 </code></pre>
33246      * @param {Object} regions An object containing ContentPanel configs by region name
33247      */
33248     batchAdd : function(regions){
33249         this.beginUpdate();
33250         for(var rname in regions){
33251             var lr = this.regions[rname];
33252             if(lr){
33253                 this.addTypedPanels(lr, regions[rname]);
33254             }
33255         }
33256         this.endUpdate();
33257     },
33258
33259     // private
33260     addTypedPanels : function(lr, ps){
33261         if(typeof ps == 'string'){
33262             lr.add(new Roo.ContentPanel(ps));
33263         }
33264         else if(ps instanceof Array){
33265             for(var i =0, len = ps.length; i < len; i++){
33266                 this.addTypedPanels(lr, ps[i]);
33267             }
33268         }
33269         else if(!ps.events){ // raw config?
33270             var el = ps.el;
33271             delete ps.el; // prevent conflict
33272             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
33273         }
33274         else {  // panel object assumed!
33275             lr.add(ps);
33276         }
33277     },
33278     /**
33279      * Adds a xtype elements to the layout.
33280      * <pre><code>
33281
33282 layout.addxtype({
33283        xtype : 'ContentPanel',
33284        region: 'west',
33285        items: [ .... ]
33286    }
33287 );
33288
33289 layout.addxtype({
33290         xtype : 'NestedLayoutPanel',
33291         region: 'west',
33292         layout: {
33293            center: { },
33294            west: { }   
33295         },
33296         items : [ ... list of content panels or nested layout panels.. ]
33297    }
33298 );
33299 </code></pre>
33300      * @param {Object} cfg Xtype definition of item to add.
33301      */
33302     addxtype : function(cfg)
33303     {
33304         // basically accepts a pannel...
33305         // can accept a layout region..!?!?
33306         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
33307         
33308         if (!cfg.xtype.match(/Panel$/)) {
33309             return false;
33310         }
33311         var ret = false;
33312         
33313         if (typeof(cfg.region) == 'undefined') {
33314             Roo.log("Failed to add Panel, region was not set");
33315             Roo.log(cfg);
33316             return false;
33317         }
33318         var region = cfg.region;
33319         delete cfg.region;
33320         
33321           
33322         var xitems = [];
33323         if (cfg.items) {
33324             xitems = cfg.items;
33325             delete cfg.items;
33326         }
33327         var nb = false;
33328         
33329         switch(cfg.xtype) 
33330         {
33331             case 'ContentPanel':  // ContentPanel (el, cfg)
33332             case 'ScrollPanel':  // ContentPanel (el, cfg)
33333             case 'ViewPanel': 
33334                 if(cfg.autoCreate) {
33335                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33336                 } else {
33337                     var el = this.el.createChild();
33338                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
33339                 }
33340                 
33341                 this.add(region, ret);
33342                 break;
33343             
33344             
33345             case 'TreePanel': // our new panel!
33346                 cfg.el = this.el.createChild();
33347                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33348                 this.add(region, ret);
33349                 break;
33350             
33351             case 'NestedLayoutPanel': 
33352                 // create a new Layout (which is  a Border Layout...
33353                 var el = this.el.createChild();
33354                 var clayout = cfg.layout;
33355                 delete cfg.layout;
33356                 clayout.items   = clayout.items  || [];
33357                 // replace this exitems with the clayout ones..
33358                 xitems = clayout.items;
33359                  
33360                 
33361                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
33362                     cfg.background = false;
33363                 }
33364                 var layout = new Roo.BorderLayout(el, clayout);
33365                 
33366                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
33367                 //console.log('adding nested layout panel '  + cfg.toSource());
33368                 this.add(region, ret);
33369                 nb = {}; /// find first...
33370                 break;
33371                 
33372             case 'GridPanel': 
33373             
33374                 // needs grid and region
33375                 
33376                 //var el = this.getRegion(region).el.createChild();
33377                 var el = this.el.createChild();
33378                 // create the grid first...
33379                 
33380                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
33381                 delete cfg.grid;
33382                 if (region == 'center' && this.active ) {
33383                     cfg.background = false;
33384                 }
33385                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
33386                 
33387                 this.add(region, ret);
33388                 if (cfg.background) {
33389                     ret.on('activate', function(gp) {
33390                         if (!gp.grid.rendered) {
33391                             gp.grid.render();
33392                         }
33393                     });
33394                 } else {
33395                     grid.render();
33396                 }
33397                 break;
33398            
33399            
33400            
33401                 
33402                 
33403                 
33404             default:
33405                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
33406                     
33407                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33408                     this.add(region, ret);
33409                 } else {
33410                 
33411                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
33412                     return null;
33413                 }
33414                 
33415              // GridPanel (grid, cfg)
33416             
33417         }
33418         this.beginUpdate();
33419         // add children..
33420         var region = '';
33421         var abn = {};
33422         Roo.each(xitems, function(i)  {
33423             region = nb && i.region ? i.region : false;
33424             
33425             var add = ret.addxtype(i);
33426            
33427             if (region) {
33428                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
33429                 if (!i.background) {
33430                     abn[region] = nb[region] ;
33431                 }
33432             }
33433             
33434         });
33435         this.endUpdate();
33436
33437         // make the last non-background panel active..
33438         //if (nb) { Roo.log(abn); }
33439         if (nb) {
33440             
33441             for(var r in abn) {
33442                 region = this.getRegion(r);
33443                 if (region) {
33444                     // tried using nb[r], but it does not work..
33445                      
33446                     region.showPanel(abn[r]);
33447                    
33448                 }
33449             }
33450         }
33451         return ret;
33452         
33453     }
33454 });
33455
33456 /**
33457  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
33458  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
33459  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
33460  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
33461  * <pre><code>
33462 // shorthand
33463 var CP = Roo.ContentPanel;
33464
33465 var layout = Roo.BorderLayout.create({
33466     north: {
33467         initialSize: 25,
33468         titlebar: false,
33469         panels: [new CP("north", "North")]
33470     },
33471     west: {
33472         split:true,
33473         initialSize: 200,
33474         minSize: 175,
33475         maxSize: 400,
33476         titlebar: true,
33477         collapsible: true,
33478         panels: [new CP("west", {title: "West"})]
33479     },
33480     east: {
33481         split:true,
33482         initialSize: 202,
33483         minSize: 175,
33484         maxSize: 400,
33485         titlebar: true,
33486         collapsible: true,
33487         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
33488     },
33489     south: {
33490         split:true,
33491         initialSize: 100,
33492         minSize: 100,
33493         maxSize: 200,
33494         titlebar: true,
33495         collapsible: true,
33496         panels: [new CP("south", {title: "South", closable: true})]
33497     },
33498     center: {
33499         titlebar: true,
33500         autoScroll:true,
33501         resizeTabs: true,
33502         minTabWidth: 50,
33503         preferredTabWidth: 150,
33504         panels: [
33505             new CP("center1", {title: "Close Me", closable: true}),
33506             new CP("center2", {title: "Center Panel", closable: false})
33507         ]
33508     }
33509 }, document.body);
33510
33511 layout.getRegion("center").showPanel("center1");
33512 </code></pre>
33513  * @param config
33514  * @param targetEl
33515  */
33516 Roo.BorderLayout.create = function(config, targetEl){
33517     var layout = new Roo.BorderLayout(targetEl || document.body, config);
33518     layout.beginUpdate();
33519     var regions = Roo.BorderLayout.RegionFactory.validRegions;
33520     for(var j = 0, jlen = regions.length; j < jlen; j++){
33521         var lr = regions[j];
33522         if(layout.regions[lr] && config[lr].panels){
33523             var r = layout.regions[lr];
33524             var ps = config[lr].panels;
33525             layout.addTypedPanels(r, ps);
33526         }
33527     }
33528     layout.endUpdate();
33529     return layout;
33530 };
33531
33532 // private
33533 Roo.BorderLayout.RegionFactory = {
33534     // private
33535     validRegions : ["north","south","east","west","center"],
33536
33537     // private
33538     create : function(target, mgr, config){
33539         target = target.toLowerCase();
33540         if(config.lightweight || config.basic){
33541             return new Roo.BasicLayoutRegion(mgr, config, target);
33542         }
33543         switch(target){
33544             case "north":
33545                 return new Roo.NorthLayoutRegion(mgr, config);
33546             case "south":
33547                 return new Roo.SouthLayoutRegion(mgr, config);
33548             case "east":
33549                 return new Roo.EastLayoutRegion(mgr, config);
33550             case "west":
33551                 return new Roo.WestLayoutRegion(mgr, config);
33552             case "center":
33553                 return new Roo.CenterLayoutRegion(mgr, config);
33554         }
33555         throw 'Layout region "'+target+'" not supported.';
33556     }
33557 };/*
33558  * Based on:
33559  * Ext JS Library 1.1.1
33560  * Copyright(c) 2006-2007, Ext JS, LLC.
33561  *
33562  * Originally Released Under LGPL - original licence link has changed is not relivant.
33563  *
33564  * Fork - LGPL
33565  * <script type="text/javascript">
33566  */
33567  
33568 /**
33569  * @class Roo.BasicLayoutRegion
33570  * @extends Roo.util.Observable
33571  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
33572  * and does not have a titlebar, tabs or any other features. All it does is size and position 
33573  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
33574  */
33575 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
33576     this.mgr = mgr;
33577     this.position  = pos;
33578     this.events = {
33579         /**
33580          * @scope Roo.BasicLayoutRegion
33581          */
33582         
33583         /**
33584          * @event beforeremove
33585          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
33586          * @param {Roo.LayoutRegion} this
33587          * @param {Roo.ContentPanel} panel The panel
33588          * @param {Object} e The cancel event object
33589          */
33590         "beforeremove" : true,
33591         /**
33592          * @event invalidated
33593          * Fires when the layout for this region is changed.
33594          * @param {Roo.LayoutRegion} this
33595          */
33596         "invalidated" : true,
33597         /**
33598          * @event visibilitychange
33599          * Fires when this region is shown or hidden 
33600          * @param {Roo.LayoutRegion} this
33601          * @param {Boolean} visibility true or false
33602          */
33603         "visibilitychange" : true,
33604         /**
33605          * @event paneladded
33606          * Fires when a panel is added. 
33607          * @param {Roo.LayoutRegion} this
33608          * @param {Roo.ContentPanel} panel The panel
33609          */
33610         "paneladded" : true,
33611         /**
33612          * @event panelremoved
33613          * Fires when a panel is removed. 
33614          * @param {Roo.LayoutRegion} this
33615          * @param {Roo.ContentPanel} panel The panel
33616          */
33617         "panelremoved" : true,
33618         /**
33619          * @event collapsed
33620          * Fires when this region is collapsed.
33621          * @param {Roo.LayoutRegion} this
33622          */
33623         "collapsed" : true,
33624         /**
33625          * @event expanded
33626          * Fires when this region is expanded.
33627          * @param {Roo.LayoutRegion} this
33628          */
33629         "expanded" : true,
33630         /**
33631          * @event slideshow
33632          * Fires when this region is slid into view.
33633          * @param {Roo.LayoutRegion} this
33634          */
33635         "slideshow" : true,
33636         /**
33637          * @event slidehide
33638          * Fires when this region slides out of view. 
33639          * @param {Roo.LayoutRegion} this
33640          */
33641         "slidehide" : true,
33642         /**
33643          * @event panelactivated
33644          * Fires when a panel is activated. 
33645          * @param {Roo.LayoutRegion} this
33646          * @param {Roo.ContentPanel} panel The activated panel
33647          */
33648         "panelactivated" : true,
33649         /**
33650          * @event resized
33651          * Fires when the user resizes this region. 
33652          * @param {Roo.LayoutRegion} this
33653          * @param {Number} newSize The new size (width for east/west, height for north/south)
33654          */
33655         "resized" : true
33656     };
33657     /** A collection of panels in this region. @type Roo.util.MixedCollection */
33658     this.panels = new Roo.util.MixedCollection();
33659     this.panels.getKey = this.getPanelId.createDelegate(this);
33660     this.box = null;
33661     this.activePanel = null;
33662     // ensure listeners are added...
33663     
33664     if (config.listeners || config.events) {
33665         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
33666             listeners : config.listeners || {},
33667             events : config.events || {}
33668         });
33669     }
33670     
33671     if(skipConfig !== true){
33672         this.applyConfig(config);
33673     }
33674 };
33675
33676 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
33677     getPanelId : function(p){
33678         return p.getId();
33679     },
33680     
33681     applyConfig : function(config){
33682         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33683         this.config = config;
33684         
33685     },
33686     
33687     /**
33688      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
33689      * the width, for horizontal (north, south) the height.
33690      * @param {Number} newSize The new width or height
33691      */
33692     resizeTo : function(newSize){
33693         var el = this.el ? this.el :
33694                  (this.activePanel ? this.activePanel.getEl() : null);
33695         if(el){
33696             switch(this.position){
33697                 case "east":
33698                 case "west":
33699                     el.setWidth(newSize);
33700                     this.fireEvent("resized", this, newSize);
33701                 break;
33702                 case "north":
33703                 case "south":
33704                     el.setHeight(newSize);
33705                     this.fireEvent("resized", this, newSize);
33706                 break;                
33707             }
33708         }
33709     },
33710     
33711     getBox : function(){
33712         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33713     },
33714     
33715     getMargins : function(){
33716         return this.margins;
33717     },
33718     
33719     updateBox : function(box){
33720         this.box = box;
33721         var el = this.activePanel.getEl();
33722         el.dom.style.left = box.x + "px";
33723         el.dom.style.top = box.y + "px";
33724         this.activePanel.setSize(box.width, box.height);
33725     },
33726     
33727     /**
33728      * Returns the container element for this region.
33729      * @return {Roo.Element}
33730      */
33731     getEl : function(){
33732         return this.activePanel;
33733     },
33734     
33735     /**
33736      * Returns true if this region is currently visible.
33737      * @return {Boolean}
33738      */
33739     isVisible : function(){
33740         return this.activePanel ? true : false;
33741     },
33742     
33743     setActivePanel : function(panel){
33744         panel = this.getPanel(panel);
33745         if(this.activePanel && this.activePanel != panel){
33746             this.activePanel.setActiveState(false);
33747             this.activePanel.getEl().setLeftTop(-10000,-10000);
33748         }
33749         this.activePanel = panel;
33750         panel.setActiveState(true);
33751         if(this.box){
33752             panel.setSize(this.box.width, this.box.height);
33753         }
33754         this.fireEvent("panelactivated", this, panel);
33755         this.fireEvent("invalidated");
33756     },
33757     
33758     /**
33759      * Show the specified panel.
33760      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33761      * @return {Roo.ContentPanel} The shown panel or null
33762      */
33763     showPanel : function(panel){
33764         if(panel = this.getPanel(panel)){
33765             this.setActivePanel(panel);
33766         }
33767         return panel;
33768     },
33769     
33770     /**
33771      * Get the active panel for this region.
33772      * @return {Roo.ContentPanel} The active panel or null
33773      */
33774     getActivePanel : function(){
33775         return this.activePanel;
33776     },
33777     
33778     /**
33779      * Add the passed ContentPanel(s)
33780      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33781      * @return {Roo.ContentPanel} The panel added (if only one was added)
33782      */
33783     add : function(panel){
33784         if(arguments.length > 1){
33785             for(var i = 0, len = arguments.length; i < len; i++) {
33786                 this.add(arguments[i]);
33787             }
33788             return null;
33789         }
33790         if(this.hasPanel(panel)){
33791             this.showPanel(panel);
33792             return panel;
33793         }
33794         var el = panel.getEl();
33795         if(el.dom.parentNode != this.mgr.el.dom){
33796             this.mgr.el.dom.appendChild(el.dom);
33797         }
33798         if(panel.setRegion){
33799             panel.setRegion(this);
33800         }
33801         this.panels.add(panel);
33802         el.setStyle("position", "absolute");
33803         if(!panel.background){
33804             this.setActivePanel(panel);
33805             if(this.config.initialSize && this.panels.getCount()==1){
33806                 this.resizeTo(this.config.initialSize);
33807             }
33808         }
33809         this.fireEvent("paneladded", this, panel);
33810         return panel;
33811     },
33812     
33813     /**
33814      * Returns true if the panel is in this region.
33815      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33816      * @return {Boolean}
33817      */
33818     hasPanel : function(panel){
33819         if(typeof panel == "object"){ // must be panel obj
33820             panel = panel.getId();
33821         }
33822         return this.getPanel(panel) ? true : false;
33823     },
33824     
33825     /**
33826      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33827      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33828      * @param {Boolean} preservePanel Overrides the config preservePanel option
33829      * @return {Roo.ContentPanel} The panel that was removed
33830      */
33831     remove : function(panel, preservePanel){
33832         panel = this.getPanel(panel);
33833         if(!panel){
33834             return null;
33835         }
33836         var e = {};
33837         this.fireEvent("beforeremove", this, panel, e);
33838         if(e.cancel === true){
33839             return null;
33840         }
33841         var panelId = panel.getId();
33842         this.panels.removeKey(panelId);
33843         return panel;
33844     },
33845     
33846     /**
33847      * Returns the panel specified or null if it's not in this region.
33848      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33849      * @return {Roo.ContentPanel}
33850      */
33851     getPanel : function(id){
33852         if(typeof id == "object"){ // must be panel obj
33853             return id;
33854         }
33855         return this.panels.get(id);
33856     },
33857     
33858     /**
33859      * Returns this regions position (north/south/east/west/center).
33860      * @return {String} 
33861      */
33862     getPosition: function(){
33863         return this.position;    
33864     }
33865 });/*
33866  * Based on:
33867  * Ext JS Library 1.1.1
33868  * Copyright(c) 2006-2007, Ext JS, LLC.
33869  *
33870  * Originally Released Under LGPL - original licence link has changed is not relivant.
33871  *
33872  * Fork - LGPL
33873  * <script type="text/javascript">
33874  */
33875  
33876 /**
33877  * @class Roo.LayoutRegion
33878  * @extends Roo.BasicLayoutRegion
33879  * This class represents a region in a layout manager.
33880  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
33881  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
33882  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
33883  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33884  * @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})
33885  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
33886  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
33887  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
33888  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
33889  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
33890  * @cfg {String}    title           The title for the region (overrides panel titles)
33891  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
33892  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33893  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
33894  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33895  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
33896  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33897  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
33898  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
33899  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
33900  * @cfg {Boolean}   showPin         True to show a pin button
33901  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
33902  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
33903  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
33904  * @cfg {Number}    width           For East/West panels
33905  * @cfg {Number}    height          For North/South panels
33906  * @cfg {Boolean}   split           To show the splitter
33907  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
33908  */
33909 Roo.LayoutRegion = function(mgr, config, pos){
33910     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
33911     var dh = Roo.DomHelper;
33912     /** This region's container element 
33913     * @type Roo.Element */
33914     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
33915     /** This region's title element 
33916     * @type Roo.Element */
33917
33918     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
33919         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
33920         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
33921     ]}, true);
33922     this.titleEl.enableDisplayMode();
33923     /** This region's title text element 
33924     * @type HTMLElement */
33925     this.titleTextEl = this.titleEl.dom.firstChild;
33926     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33927     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
33928     this.closeBtn.enableDisplayMode();
33929     this.closeBtn.on("click", this.closeClicked, this);
33930     this.closeBtn.hide();
33931
33932     this.createBody(config);
33933     this.visible = true;
33934     this.collapsed = false;
33935
33936     if(config.hideWhenEmpty){
33937         this.hide();
33938         this.on("paneladded", this.validateVisibility, this);
33939         this.on("panelremoved", this.validateVisibility, this);
33940     }
33941     this.applyConfig(config);
33942 };
33943
33944 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
33945
33946     createBody : function(){
33947         /** This region's body element 
33948         * @type Roo.Element */
33949         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
33950     },
33951
33952     applyConfig : function(c){
33953         if(c.collapsible && this.position != "center" && !this.collapsedEl){
33954             var dh = Roo.DomHelper;
33955             if(c.titlebar !== false){
33956                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
33957                 this.collapseBtn.on("click", this.collapse, this);
33958                 this.collapseBtn.enableDisplayMode();
33959
33960                 if(c.showPin === true || this.showPin){
33961                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
33962                     this.stickBtn.enableDisplayMode();
33963                     this.stickBtn.on("click", this.expand, this);
33964                     this.stickBtn.hide();
33965                 }
33966             }
33967             /** This region's collapsed element
33968             * @type Roo.Element */
33969             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33970                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33971             ]}, true);
33972             if(c.floatable !== false){
33973                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33974                this.collapsedEl.on("click", this.collapseClick, this);
33975             }
33976
33977             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33978                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33979                    id: "message", unselectable: "on", style:{"float":"left"}});
33980                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33981              }
33982             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33983             this.expandBtn.on("click", this.expand, this);
33984         }
33985         if(this.collapseBtn){
33986             this.collapseBtn.setVisible(c.collapsible == true);
33987         }
33988         this.cmargins = c.cmargins || this.cmargins ||
33989                          (this.position == "west" || this.position == "east" ?
33990                              {top: 0, left: 2, right:2, bottom: 0} :
33991                              {top: 2, left: 0, right:0, bottom: 2});
33992         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33993         this.bottomTabs = c.tabPosition != "top";
33994         this.autoScroll = c.autoScroll || false;
33995         if(this.autoScroll){
33996             this.bodyEl.setStyle("overflow", "auto");
33997         }else{
33998             this.bodyEl.setStyle("overflow", "hidden");
33999         }
34000         //if(c.titlebar !== false){
34001             if((!c.titlebar && !c.title) || c.titlebar === false){
34002                 this.titleEl.hide();
34003             }else{
34004                 this.titleEl.show();
34005                 if(c.title){
34006                     this.titleTextEl.innerHTML = c.title;
34007                 }
34008             }
34009         //}
34010         this.duration = c.duration || .30;
34011         this.slideDuration = c.slideDuration || .45;
34012         this.config = c;
34013         if(c.collapsed){
34014             this.collapse(true);
34015         }
34016         if(c.hidden){
34017             this.hide();
34018         }
34019     },
34020     /**
34021      * Returns true if this region is currently visible.
34022      * @return {Boolean}
34023      */
34024     isVisible : function(){
34025         return this.visible;
34026     },
34027
34028     /**
34029      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
34030      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
34031      */
34032     setCollapsedTitle : function(title){
34033         title = title || "&#160;";
34034         if(this.collapsedTitleTextEl){
34035             this.collapsedTitleTextEl.innerHTML = title;
34036         }
34037     },
34038
34039     getBox : function(){
34040         var b;
34041         if(!this.collapsed){
34042             b = this.el.getBox(false, true);
34043         }else{
34044             b = this.collapsedEl.getBox(false, true);
34045         }
34046         return b;
34047     },
34048
34049     getMargins : function(){
34050         return this.collapsed ? this.cmargins : this.margins;
34051     },
34052
34053     highlight : function(){
34054         this.el.addClass("x-layout-panel-dragover");
34055     },
34056
34057     unhighlight : function(){
34058         this.el.removeClass("x-layout-panel-dragover");
34059     },
34060
34061     updateBox : function(box){
34062         this.box = box;
34063         if(!this.collapsed){
34064             this.el.dom.style.left = box.x + "px";
34065             this.el.dom.style.top = box.y + "px";
34066             this.updateBody(box.width, box.height);
34067         }else{
34068             this.collapsedEl.dom.style.left = box.x + "px";
34069             this.collapsedEl.dom.style.top = box.y + "px";
34070             this.collapsedEl.setSize(box.width, box.height);
34071         }
34072         if(this.tabs){
34073             this.tabs.autoSizeTabs();
34074         }
34075     },
34076
34077     updateBody : function(w, h){
34078         if(w !== null){
34079             this.el.setWidth(w);
34080             w -= this.el.getBorderWidth("rl");
34081             if(this.config.adjustments){
34082                 w += this.config.adjustments[0];
34083             }
34084         }
34085         if(h !== null){
34086             this.el.setHeight(h);
34087             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
34088             h -= this.el.getBorderWidth("tb");
34089             if(this.config.adjustments){
34090                 h += this.config.adjustments[1];
34091             }
34092             this.bodyEl.setHeight(h);
34093             if(this.tabs){
34094                 h = this.tabs.syncHeight(h);
34095             }
34096         }
34097         if(this.panelSize){
34098             w = w !== null ? w : this.panelSize.width;
34099             h = h !== null ? h : this.panelSize.height;
34100         }
34101         if(this.activePanel){
34102             var el = this.activePanel.getEl();
34103             w = w !== null ? w : el.getWidth();
34104             h = h !== null ? h : el.getHeight();
34105             this.panelSize = {width: w, height: h};
34106             this.activePanel.setSize(w, h);
34107         }
34108         if(Roo.isIE && this.tabs){
34109             this.tabs.el.repaint();
34110         }
34111     },
34112
34113     /**
34114      * Returns the container element for this region.
34115      * @return {Roo.Element}
34116      */
34117     getEl : function(){
34118         return this.el;
34119     },
34120
34121     /**
34122      * Hides this region.
34123      */
34124     hide : function(){
34125         if(!this.collapsed){
34126             this.el.dom.style.left = "-2000px";
34127             this.el.hide();
34128         }else{
34129             this.collapsedEl.dom.style.left = "-2000px";
34130             this.collapsedEl.hide();
34131         }
34132         this.visible = false;
34133         this.fireEvent("visibilitychange", this, false);
34134     },
34135
34136     /**
34137      * Shows this region if it was previously hidden.
34138      */
34139     show : function(){
34140         if(!this.collapsed){
34141             this.el.show();
34142         }else{
34143             this.collapsedEl.show();
34144         }
34145         this.visible = true;
34146         this.fireEvent("visibilitychange", this, true);
34147     },
34148
34149     closeClicked : function(){
34150         if(this.activePanel){
34151             this.remove(this.activePanel);
34152         }
34153     },
34154
34155     collapseClick : function(e){
34156         if(this.isSlid){
34157            e.stopPropagation();
34158            this.slideIn();
34159         }else{
34160            e.stopPropagation();
34161            this.slideOut();
34162         }
34163     },
34164
34165     /**
34166      * Collapses this region.
34167      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
34168      */
34169     collapse : function(skipAnim){
34170         if(this.collapsed) {
34171             return;
34172         }
34173         this.collapsed = true;
34174         if(this.split){
34175             this.split.el.hide();
34176         }
34177         if(this.config.animate && skipAnim !== true){
34178             this.fireEvent("invalidated", this);
34179             this.animateCollapse();
34180         }else{
34181             this.el.setLocation(-20000,-20000);
34182             this.el.hide();
34183             this.collapsedEl.show();
34184             this.fireEvent("collapsed", this);
34185             this.fireEvent("invalidated", this);
34186         }
34187     },
34188
34189     animateCollapse : function(){
34190         // overridden
34191     },
34192
34193     /**
34194      * Expands this region if it was previously collapsed.
34195      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
34196      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
34197      */
34198     expand : function(e, skipAnim){
34199         if(e) {
34200             e.stopPropagation();
34201         }
34202         if(!this.collapsed || this.el.hasActiveFx()) {
34203             return;
34204         }
34205         if(this.isSlid){
34206             this.afterSlideIn();
34207             skipAnim = true;
34208         }
34209         this.collapsed = false;
34210         if(this.config.animate && skipAnim !== true){
34211             this.animateExpand();
34212         }else{
34213             this.el.show();
34214             if(this.split){
34215                 this.split.el.show();
34216             }
34217             this.collapsedEl.setLocation(-2000,-2000);
34218             this.collapsedEl.hide();
34219             this.fireEvent("invalidated", this);
34220             this.fireEvent("expanded", this);
34221         }
34222     },
34223
34224     animateExpand : function(){
34225         // overridden
34226     },
34227
34228     initTabs : function()
34229     {
34230         this.bodyEl.setStyle("overflow", "hidden");
34231         var ts = new Roo.TabPanel(
34232                 this.bodyEl.dom,
34233                 {
34234                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
34235                     disableTooltips: this.config.disableTabTips,
34236                     toolbar : this.config.toolbar
34237                 }
34238         );
34239         if(this.config.hideTabs){
34240             ts.stripWrap.setDisplayed(false);
34241         }
34242         this.tabs = ts;
34243         ts.resizeTabs = this.config.resizeTabs === true;
34244         ts.minTabWidth = this.config.minTabWidth || 40;
34245         ts.maxTabWidth = this.config.maxTabWidth || 250;
34246         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
34247         ts.monitorResize = false;
34248         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34249         ts.bodyEl.addClass('x-layout-tabs-body');
34250         this.panels.each(this.initPanelAsTab, this);
34251     },
34252
34253     initPanelAsTab : function(panel){
34254         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
34255                     this.config.closeOnTab && panel.isClosable());
34256         if(panel.tabTip !== undefined){
34257             ti.setTooltip(panel.tabTip);
34258         }
34259         ti.on("activate", function(){
34260               this.setActivePanel(panel);
34261         }, this);
34262         if(this.config.closeOnTab){
34263             ti.on("beforeclose", function(t, e){
34264                 e.cancel = true;
34265                 this.remove(panel);
34266             }, this);
34267         }
34268         return ti;
34269     },
34270
34271     updatePanelTitle : function(panel, title){
34272         if(this.activePanel == panel){
34273             this.updateTitle(title);
34274         }
34275         if(this.tabs){
34276             var ti = this.tabs.getTab(panel.getEl().id);
34277             ti.setText(title);
34278             if(panel.tabTip !== undefined){
34279                 ti.setTooltip(panel.tabTip);
34280             }
34281         }
34282     },
34283
34284     updateTitle : function(title){
34285         if(this.titleTextEl && !this.config.title){
34286             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
34287         }
34288     },
34289
34290     setActivePanel : function(panel){
34291         panel = this.getPanel(panel);
34292         if(this.activePanel && this.activePanel != panel){
34293             this.activePanel.setActiveState(false);
34294         }
34295         this.activePanel = panel;
34296         panel.setActiveState(true);
34297         if(this.panelSize){
34298             panel.setSize(this.panelSize.width, this.panelSize.height);
34299         }
34300         if(this.closeBtn){
34301             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
34302         }
34303         this.updateTitle(panel.getTitle());
34304         if(this.tabs){
34305             this.fireEvent("invalidated", this);
34306         }
34307         this.fireEvent("panelactivated", this, panel);
34308     },
34309
34310     /**
34311      * Shows the specified panel.
34312      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
34313      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
34314      */
34315     showPanel : function(panel)
34316     {
34317         panel = this.getPanel(panel);
34318         if(panel){
34319             if(this.tabs){
34320                 var tab = this.tabs.getTab(panel.getEl().id);
34321                 if(tab.isHidden()){
34322                     this.tabs.unhideTab(tab.id);
34323                 }
34324                 tab.activate();
34325             }else{
34326                 this.setActivePanel(panel);
34327             }
34328         }
34329         return panel;
34330     },
34331
34332     /**
34333      * Get the active panel for this region.
34334      * @return {Roo.ContentPanel} The active panel or null
34335      */
34336     getActivePanel : function(){
34337         return this.activePanel;
34338     },
34339
34340     validateVisibility : function(){
34341         if(this.panels.getCount() < 1){
34342             this.updateTitle("&#160;");
34343             this.closeBtn.hide();
34344             this.hide();
34345         }else{
34346             if(!this.isVisible()){
34347                 this.show();
34348             }
34349         }
34350     },
34351
34352     /**
34353      * Adds the passed ContentPanel(s) to this region.
34354      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34355      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
34356      */
34357     add : function(panel){
34358         if(arguments.length > 1){
34359             for(var i = 0, len = arguments.length; i < len; i++) {
34360                 this.add(arguments[i]);
34361             }
34362             return null;
34363         }
34364         if(this.hasPanel(panel)){
34365             this.showPanel(panel);
34366             return panel;
34367         }
34368         panel.setRegion(this);
34369         this.panels.add(panel);
34370         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
34371             this.bodyEl.dom.appendChild(panel.getEl().dom);
34372             if(panel.background !== true){
34373                 this.setActivePanel(panel);
34374             }
34375             this.fireEvent("paneladded", this, panel);
34376             return panel;
34377         }
34378         if(!this.tabs){
34379             this.initTabs();
34380         }else{
34381             this.initPanelAsTab(panel);
34382         }
34383         if(panel.background !== true){
34384             this.tabs.activate(panel.getEl().id);
34385         }
34386         this.fireEvent("paneladded", this, panel);
34387         return panel;
34388     },
34389
34390     /**
34391      * Hides the tab for the specified panel.
34392      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34393      */
34394     hidePanel : function(panel){
34395         if(this.tabs && (panel = this.getPanel(panel))){
34396             this.tabs.hideTab(panel.getEl().id);
34397         }
34398     },
34399
34400     /**
34401      * Unhides the tab for a previously hidden panel.
34402      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34403      */
34404     unhidePanel : function(panel){
34405         if(this.tabs && (panel = this.getPanel(panel))){
34406             this.tabs.unhideTab(panel.getEl().id);
34407         }
34408     },
34409
34410     clearPanels : function(){
34411         while(this.panels.getCount() > 0){
34412              this.remove(this.panels.first());
34413         }
34414     },
34415
34416     /**
34417      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34418      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34419      * @param {Boolean} preservePanel Overrides the config preservePanel option
34420      * @return {Roo.ContentPanel} The panel that was removed
34421      */
34422     remove : function(panel, preservePanel){
34423         panel = this.getPanel(panel);
34424         if(!panel){
34425             return null;
34426         }
34427         var e = {};
34428         this.fireEvent("beforeremove", this, panel, e);
34429         if(e.cancel === true){
34430             return null;
34431         }
34432         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
34433         var panelId = panel.getId();
34434         this.panels.removeKey(panelId);
34435         if(preservePanel){
34436             document.body.appendChild(panel.getEl().dom);
34437         }
34438         if(this.tabs){
34439             this.tabs.removeTab(panel.getEl().id);
34440         }else if (!preservePanel){
34441             this.bodyEl.dom.removeChild(panel.getEl().dom);
34442         }
34443         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
34444             var p = this.panels.first();
34445             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
34446             tempEl.appendChild(p.getEl().dom);
34447             this.bodyEl.update("");
34448             this.bodyEl.dom.appendChild(p.getEl().dom);
34449             tempEl = null;
34450             this.updateTitle(p.getTitle());
34451             this.tabs = null;
34452             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34453             this.setActivePanel(p);
34454         }
34455         panel.setRegion(null);
34456         if(this.activePanel == panel){
34457             this.activePanel = null;
34458         }
34459         if(this.config.autoDestroy !== false && preservePanel !== true){
34460             try{panel.destroy();}catch(e){}
34461         }
34462         this.fireEvent("panelremoved", this, panel);
34463         return panel;
34464     },
34465
34466     /**
34467      * Returns the TabPanel component used by this region
34468      * @return {Roo.TabPanel}
34469      */
34470     getTabs : function(){
34471         return this.tabs;
34472     },
34473
34474     createTool : function(parentEl, className){
34475         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
34476             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
34477         btn.addClassOnOver("x-layout-tools-button-over");
34478         return btn;
34479     }
34480 });/*
34481  * Based on:
34482  * Ext JS Library 1.1.1
34483  * Copyright(c) 2006-2007, Ext JS, LLC.
34484  *
34485  * Originally Released Under LGPL - original licence link has changed is not relivant.
34486  *
34487  * Fork - LGPL
34488  * <script type="text/javascript">
34489  */
34490  
34491
34492
34493 /**
34494  * @class Roo.SplitLayoutRegion
34495  * @extends Roo.LayoutRegion
34496  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
34497  */
34498 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
34499     this.cursor = cursor;
34500     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
34501 };
34502
34503 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
34504     splitTip : "Drag to resize.",
34505     collapsibleSplitTip : "Drag to resize. Double click to hide.",
34506     useSplitTips : false,
34507
34508     applyConfig : function(config){
34509         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
34510         if(config.split){
34511             if(!this.split){
34512                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
34513                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
34514                 /** The SplitBar for this region 
34515                 * @type Roo.SplitBar */
34516                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
34517                 this.split.on("moved", this.onSplitMove, this);
34518                 this.split.useShim = config.useShim === true;
34519                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
34520                 if(this.useSplitTips){
34521                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
34522                 }
34523                 if(config.collapsible){
34524                     this.split.el.on("dblclick", this.collapse,  this);
34525                 }
34526             }
34527             if(typeof config.minSize != "undefined"){
34528                 this.split.minSize = config.minSize;
34529             }
34530             if(typeof config.maxSize != "undefined"){
34531                 this.split.maxSize = config.maxSize;
34532             }
34533             if(config.hideWhenEmpty || config.hidden || config.collapsed){
34534                 this.hideSplitter();
34535             }
34536         }
34537     },
34538
34539     getHMaxSize : function(){
34540          var cmax = this.config.maxSize || 10000;
34541          var center = this.mgr.getRegion("center");
34542          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
34543     },
34544
34545     getVMaxSize : function(){
34546          var cmax = this.config.maxSize || 10000;
34547          var center = this.mgr.getRegion("center");
34548          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
34549     },
34550
34551     onSplitMove : function(split, newSize){
34552         this.fireEvent("resized", this, newSize);
34553     },
34554     
34555     /** 
34556      * Returns the {@link Roo.SplitBar} for this region.
34557      * @return {Roo.SplitBar}
34558      */
34559     getSplitBar : function(){
34560         return this.split;
34561     },
34562     
34563     hide : function(){
34564         this.hideSplitter();
34565         Roo.SplitLayoutRegion.superclass.hide.call(this);
34566     },
34567
34568     hideSplitter : function(){
34569         if(this.split){
34570             this.split.el.setLocation(-2000,-2000);
34571             this.split.el.hide();
34572         }
34573     },
34574
34575     show : function(){
34576         if(this.split){
34577             this.split.el.show();
34578         }
34579         Roo.SplitLayoutRegion.superclass.show.call(this);
34580     },
34581     
34582     beforeSlide: function(){
34583         if(Roo.isGecko){// firefox overflow auto bug workaround
34584             this.bodyEl.clip();
34585             if(this.tabs) {
34586                 this.tabs.bodyEl.clip();
34587             }
34588             if(this.activePanel){
34589                 this.activePanel.getEl().clip();
34590                 
34591                 if(this.activePanel.beforeSlide){
34592                     this.activePanel.beforeSlide();
34593                 }
34594             }
34595         }
34596     },
34597     
34598     afterSlide : function(){
34599         if(Roo.isGecko){// firefox overflow auto bug workaround
34600             this.bodyEl.unclip();
34601             if(this.tabs) {
34602                 this.tabs.bodyEl.unclip();
34603             }
34604             if(this.activePanel){
34605                 this.activePanel.getEl().unclip();
34606                 if(this.activePanel.afterSlide){
34607                     this.activePanel.afterSlide();
34608                 }
34609             }
34610         }
34611     },
34612
34613     initAutoHide : function(){
34614         if(this.autoHide !== false){
34615             if(!this.autoHideHd){
34616                 var st = new Roo.util.DelayedTask(this.slideIn, this);
34617                 this.autoHideHd = {
34618                     "mouseout": function(e){
34619                         if(!e.within(this.el, true)){
34620                             st.delay(500);
34621                         }
34622                     },
34623                     "mouseover" : function(e){
34624                         st.cancel();
34625                     },
34626                     scope : this
34627                 };
34628             }
34629             this.el.on(this.autoHideHd);
34630         }
34631     },
34632
34633     clearAutoHide : function(){
34634         if(this.autoHide !== false){
34635             this.el.un("mouseout", this.autoHideHd.mouseout);
34636             this.el.un("mouseover", this.autoHideHd.mouseover);
34637         }
34638     },
34639
34640     clearMonitor : function(){
34641         Roo.get(document).un("click", this.slideInIf, this);
34642     },
34643
34644     // these names are backwards but not changed for compat
34645     slideOut : function(){
34646         if(this.isSlid || this.el.hasActiveFx()){
34647             return;
34648         }
34649         this.isSlid = true;
34650         if(this.collapseBtn){
34651             this.collapseBtn.hide();
34652         }
34653         this.closeBtnState = this.closeBtn.getStyle('display');
34654         this.closeBtn.hide();
34655         if(this.stickBtn){
34656             this.stickBtn.show();
34657         }
34658         this.el.show();
34659         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34660         this.beforeSlide();
34661         this.el.setStyle("z-index", 10001);
34662         this.el.slideIn(this.getSlideAnchor(), {
34663             callback: function(){
34664                 this.afterSlide();
34665                 this.initAutoHide();
34666                 Roo.get(document).on("click", this.slideInIf, this);
34667                 this.fireEvent("slideshow", this);
34668             },
34669             scope: this,
34670             block: true
34671         });
34672     },
34673
34674     afterSlideIn : function(){
34675         this.clearAutoHide();
34676         this.isSlid = false;
34677         this.clearMonitor();
34678         this.el.setStyle("z-index", "");
34679         if(this.collapseBtn){
34680             this.collapseBtn.show();
34681         }
34682         this.closeBtn.setStyle('display', this.closeBtnState);
34683         if(this.stickBtn){
34684             this.stickBtn.hide();
34685         }
34686         this.fireEvent("slidehide", this);
34687     },
34688
34689     slideIn : function(cb){
34690         if(!this.isSlid || this.el.hasActiveFx()){
34691             Roo.callback(cb);
34692             return;
34693         }
34694         this.isSlid = false;
34695         this.beforeSlide();
34696         this.el.slideOut(this.getSlideAnchor(), {
34697             callback: function(){
34698                 this.el.setLeftTop(-10000, -10000);
34699                 this.afterSlide();
34700                 this.afterSlideIn();
34701                 Roo.callback(cb);
34702             },
34703             scope: this,
34704             block: true
34705         });
34706     },
34707     
34708     slideInIf : function(e){
34709         if(!e.within(this.el)){
34710             this.slideIn();
34711         }
34712     },
34713
34714     animateCollapse : function(){
34715         this.beforeSlide();
34716         this.el.setStyle("z-index", 20000);
34717         var anchor = this.getSlideAnchor();
34718         this.el.slideOut(anchor, {
34719             callback : function(){
34720                 this.el.setStyle("z-index", "");
34721                 this.collapsedEl.slideIn(anchor, {duration:.3});
34722                 this.afterSlide();
34723                 this.el.setLocation(-10000,-10000);
34724                 this.el.hide();
34725                 this.fireEvent("collapsed", this);
34726             },
34727             scope: this,
34728             block: true
34729         });
34730     },
34731
34732     animateExpand : function(){
34733         this.beforeSlide();
34734         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34735         this.el.setStyle("z-index", 20000);
34736         this.collapsedEl.hide({
34737             duration:.1
34738         });
34739         this.el.slideIn(this.getSlideAnchor(), {
34740             callback : function(){
34741                 this.el.setStyle("z-index", "");
34742                 this.afterSlide();
34743                 if(this.split){
34744                     this.split.el.show();
34745                 }
34746                 this.fireEvent("invalidated", this);
34747                 this.fireEvent("expanded", this);
34748             },
34749             scope: this,
34750             block: true
34751         });
34752     },
34753
34754     anchors : {
34755         "west" : "left",
34756         "east" : "right",
34757         "north" : "top",
34758         "south" : "bottom"
34759     },
34760
34761     sanchors : {
34762         "west" : "l",
34763         "east" : "r",
34764         "north" : "t",
34765         "south" : "b"
34766     },
34767
34768     canchors : {
34769         "west" : "tl-tr",
34770         "east" : "tr-tl",
34771         "north" : "tl-bl",
34772         "south" : "bl-tl"
34773     },
34774
34775     getAnchor : function(){
34776         return this.anchors[this.position];
34777     },
34778
34779     getCollapseAnchor : function(){
34780         return this.canchors[this.position];
34781     },
34782
34783     getSlideAnchor : function(){
34784         return this.sanchors[this.position];
34785     },
34786
34787     getAlignAdj : function(){
34788         var cm = this.cmargins;
34789         switch(this.position){
34790             case "west":
34791                 return [0, 0];
34792             break;
34793             case "east":
34794                 return [0, 0];
34795             break;
34796             case "north":
34797                 return [0, 0];
34798             break;
34799             case "south":
34800                 return [0, 0];
34801             break;
34802         }
34803     },
34804
34805     getExpandAdj : function(){
34806         var c = this.collapsedEl, cm = this.cmargins;
34807         switch(this.position){
34808             case "west":
34809                 return [-(cm.right+c.getWidth()+cm.left), 0];
34810             break;
34811             case "east":
34812                 return [cm.right+c.getWidth()+cm.left, 0];
34813             break;
34814             case "north":
34815                 return [0, -(cm.top+cm.bottom+c.getHeight())];
34816             break;
34817             case "south":
34818                 return [0, cm.top+cm.bottom+c.getHeight()];
34819             break;
34820         }
34821     }
34822 });/*
34823  * Based on:
34824  * Ext JS Library 1.1.1
34825  * Copyright(c) 2006-2007, Ext JS, LLC.
34826  *
34827  * Originally Released Under LGPL - original licence link has changed is not relivant.
34828  *
34829  * Fork - LGPL
34830  * <script type="text/javascript">
34831  */
34832 /*
34833  * These classes are private internal classes
34834  */
34835 Roo.CenterLayoutRegion = function(mgr, config){
34836     Roo.LayoutRegion.call(this, mgr, config, "center");
34837     this.visible = true;
34838     this.minWidth = config.minWidth || 20;
34839     this.minHeight = config.minHeight || 20;
34840 };
34841
34842 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
34843     hide : function(){
34844         // center panel can't be hidden
34845     },
34846     
34847     show : function(){
34848         // center panel can't be hidden
34849     },
34850     
34851     getMinWidth: function(){
34852         return this.minWidth;
34853     },
34854     
34855     getMinHeight: function(){
34856         return this.minHeight;
34857     }
34858 });
34859
34860
34861 Roo.NorthLayoutRegion = function(mgr, config){
34862     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
34863     if(this.split){
34864         this.split.placement = Roo.SplitBar.TOP;
34865         this.split.orientation = Roo.SplitBar.VERTICAL;
34866         this.split.el.addClass("x-layout-split-v");
34867     }
34868     var size = config.initialSize || config.height;
34869     if(typeof size != "undefined"){
34870         this.el.setHeight(size);
34871     }
34872 };
34873 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
34874     orientation: Roo.SplitBar.VERTICAL,
34875     getBox : function(){
34876         if(this.collapsed){
34877             return this.collapsedEl.getBox();
34878         }
34879         var box = this.el.getBox();
34880         if(this.split){
34881             box.height += this.split.el.getHeight();
34882         }
34883         return box;
34884     },
34885     
34886     updateBox : function(box){
34887         if(this.split && !this.collapsed){
34888             box.height -= this.split.el.getHeight();
34889             this.split.el.setLeft(box.x);
34890             this.split.el.setTop(box.y+box.height);
34891             this.split.el.setWidth(box.width);
34892         }
34893         if(this.collapsed){
34894             this.updateBody(box.width, null);
34895         }
34896         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34897     }
34898 });
34899
34900 Roo.SouthLayoutRegion = function(mgr, config){
34901     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
34902     if(this.split){
34903         this.split.placement = Roo.SplitBar.BOTTOM;
34904         this.split.orientation = Roo.SplitBar.VERTICAL;
34905         this.split.el.addClass("x-layout-split-v");
34906     }
34907     var size = config.initialSize || config.height;
34908     if(typeof size != "undefined"){
34909         this.el.setHeight(size);
34910     }
34911 };
34912 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
34913     orientation: Roo.SplitBar.VERTICAL,
34914     getBox : function(){
34915         if(this.collapsed){
34916             return this.collapsedEl.getBox();
34917         }
34918         var box = this.el.getBox();
34919         if(this.split){
34920             var sh = this.split.el.getHeight();
34921             box.height += sh;
34922             box.y -= sh;
34923         }
34924         return box;
34925     },
34926     
34927     updateBox : function(box){
34928         if(this.split && !this.collapsed){
34929             var sh = this.split.el.getHeight();
34930             box.height -= sh;
34931             box.y += sh;
34932             this.split.el.setLeft(box.x);
34933             this.split.el.setTop(box.y-sh);
34934             this.split.el.setWidth(box.width);
34935         }
34936         if(this.collapsed){
34937             this.updateBody(box.width, null);
34938         }
34939         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34940     }
34941 });
34942
34943 Roo.EastLayoutRegion = function(mgr, config){
34944     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
34945     if(this.split){
34946         this.split.placement = Roo.SplitBar.RIGHT;
34947         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34948         this.split.el.addClass("x-layout-split-h");
34949     }
34950     var size = config.initialSize || config.width;
34951     if(typeof size != "undefined"){
34952         this.el.setWidth(size);
34953     }
34954 };
34955 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
34956     orientation: Roo.SplitBar.HORIZONTAL,
34957     getBox : function(){
34958         if(this.collapsed){
34959             return this.collapsedEl.getBox();
34960         }
34961         var box = this.el.getBox();
34962         if(this.split){
34963             var sw = this.split.el.getWidth();
34964             box.width += sw;
34965             box.x -= sw;
34966         }
34967         return box;
34968     },
34969
34970     updateBox : function(box){
34971         if(this.split && !this.collapsed){
34972             var sw = this.split.el.getWidth();
34973             box.width -= sw;
34974             this.split.el.setLeft(box.x);
34975             this.split.el.setTop(box.y);
34976             this.split.el.setHeight(box.height);
34977             box.x += sw;
34978         }
34979         if(this.collapsed){
34980             this.updateBody(null, box.height);
34981         }
34982         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34983     }
34984 });
34985
34986 Roo.WestLayoutRegion = function(mgr, config){
34987     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
34988     if(this.split){
34989         this.split.placement = Roo.SplitBar.LEFT;
34990         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34991         this.split.el.addClass("x-layout-split-h");
34992     }
34993     var size = config.initialSize || config.width;
34994     if(typeof size != "undefined"){
34995         this.el.setWidth(size);
34996     }
34997 };
34998 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
34999     orientation: Roo.SplitBar.HORIZONTAL,
35000     getBox : function(){
35001         if(this.collapsed){
35002             return this.collapsedEl.getBox();
35003         }
35004         var box = this.el.getBox();
35005         if(this.split){
35006             box.width += this.split.el.getWidth();
35007         }
35008         return box;
35009     },
35010     
35011     updateBox : function(box){
35012         if(this.split && !this.collapsed){
35013             var sw = this.split.el.getWidth();
35014             box.width -= sw;
35015             this.split.el.setLeft(box.x+box.width);
35016             this.split.el.setTop(box.y);
35017             this.split.el.setHeight(box.height);
35018         }
35019         if(this.collapsed){
35020             this.updateBody(null, box.height);
35021         }
35022         Roo.LayoutRegion.prototype.updateBox.call(this, box);
35023     }
35024 });
35025 /*
35026  * Based on:
35027  * Ext JS Library 1.1.1
35028  * Copyright(c) 2006-2007, Ext JS, LLC.
35029  *
35030  * Originally Released Under LGPL - original licence link has changed is not relivant.
35031  *
35032  * Fork - LGPL
35033  * <script type="text/javascript">
35034  */
35035  
35036  
35037 /*
35038  * Private internal class for reading and applying state
35039  */
35040 Roo.LayoutStateManager = function(layout){
35041      // default empty state
35042      this.state = {
35043         north: {},
35044         south: {},
35045         east: {},
35046         west: {}       
35047     };
35048 };
35049
35050 Roo.LayoutStateManager.prototype = {
35051     init : function(layout, provider){
35052         this.provider = provider;
35053         var state = provider.get(layout.id+"-layout-state");
35054         if(state){
35055             var wasUpdating = layout.isUpdating();
35056             if(!wasUpdating){
35057                 layout.beginUpdate();
35058             }
35059             for(var key in state){
35060                 if(typeof state[key] != "function"){
35061                     var rstate = state[key];
35062                     var r = layout.getRegion(key);
35063                     if(r && rstate){
35064                         if(rstate.size){
35065                             r.resizeTo(rstate.size);
35066                         }
35067                         if(rstate.collapsed == true){
35068                             r.collapse(true);
35069                         }else{
35070                             r.expand(null, true);
35071                         }
35072                     }
35073                 }
35074             }
35075             if(!wasUpdating){
35076                 layout.endUpdate();
35077             }
35078             this.state = state; 
35079         }
35080         this.layout = layout;
35081         layout.on("regionresized", this.onRegionResized, this);
35082         layout.on("regioncollapsed", this.onRegionCollapsed, this);
35083         layout.on("regionexpanded", this.onRegionExpanded, this);
35084     },
35085     
35086     storeState : function(){
35087         this.provider.set(this.layout.id+"-layout-state", this.state);
35088     },
35089     
35090     onRegionResized : function(region, newSize){
35091         this.state[region.getPosition()].size = newSize;
35092         this.storeState();
35093     },
35094     
35095     onRegionCollapsed : function(region){
35096         this.state[region.getPosition()].collapsed = true;
35097         this.storeState();
35098     },
35099     
35100     onRegionExpanded : function(region){
35101         this.state[region.getPosition()].collapsed = false;
35102         this.storeState();
35103     }
35104 };/*
35105  * Based on:
35106  * Ext JS Library 1.1.1
35107  * Copyright(c) 2006-2007, Ext JS, LLC.
35108  *
35109  * Originally Released Under LGPL - original licence link has changed is not relivant.
35110  *
35111  * Fork - LGPL
35112  * <script type="text/javascript">
35113  */
35114 /**
35115  * @class Roo.ContentPanel
35116  * @extends Roo.util.Observable
35117  * A basic ContentPanel element.
35118  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
35119  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
35120  * @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
35121  * @cfg {Boolean}   closable      True if the panel can be closed/removed
35122  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
35123  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
35124  * @cfg {Toolbar}   toolbar       A toolbar for this panel
35125  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
35126  * @cfg {String} title          The title for this panel
35127  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
35128  * @cfg {String} url            Calls {@link #setUrl} with this value
35129  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
35130  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
35131  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
35132  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
35133
35134  * @constructor
35135  * Create a new ContentPanel.
35136  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
35137  * @param {String/Object} config A string to set only the title or a config object
35138  * @param {String} content (optional) Set the HTML content for this panel
35139  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
35140  */
35141 Roo.ContentPanel = function(el, config, content){
35142     
35143      
35144     /*
35145     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
35146         config = el;
35147         el = Roo.id();
35148     }
35149     if (config && config.parentLayout) { 
35150         el = config.parentLayout.el.createChild(); 
35151     }
35152     */
35153     if(el.autoCreate){ // xtype is available if this is called from factory
35154         config = el;
35155         el = Roo.id();
35156     }
35157     this.el = Roo.get(el);
35158     if(!this.el && config && config.autoCreate){
35159         if(typeof config.autoCreate == "object"){
35160             if(!config.autoCreate.id){
35161                 config.autoCreate.id = config.id||el;
35162             }
35163             this.el = Roo.DomHelper.append(document.body,
35164                         config.autoCreate, true);
35165         }else{
35166             this.el = Roo.DomHelper.append(document.body,
35167                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
35168         }
35169     }
35170     this.closable = false;
35171     this.loaded = false;
35172     this.active = false;
35173     if(typeof config == "string"){
35174         this.title = config;
35175     }else{
35176         Roo.apply(this, config);
35177     }
35178     
35179     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
35180         this.wrapEl = this.el.wrap();
35181         this.toolbar.container = this.el.insertSibling(false, 'before');
35182         this.toolbar = new Roo.Toolbar(this.toolbar);
35183     }
35184     
35185     // xtype created footer. - not sure if will work as we normally have to render first..
35186     if (this.footer && !this.footer.el && this.footer.xtype) {
35187         if (!this.wrapEl) {
35188             this.wrapEl = this.el.wrap();
35189         }
35190     
35191         this.footer.container = this.wrapEl.createChild();
35192          
35193         this.footer = Roo.factory(this.footer, Roo);
35194         
35195     }
35196     
35197     if(this.resizeEl){
35198         this.resizeEl = Roo.get(this.resizeEl, true);
35199     }else{
35200         this.resizeEl = this.el;
35201     }
35202     // handle view.xtype
35203     
35204  
35205     
35206     
35207     this.addEvents({
35208         /**
35209          * @event activate
35210          * Fires when this panel is activated. 
35211          * @param {Roo.ContentPanel} this
35212          */
35213         "activate" : true,
35214         /**
35215          * @event deactivate
35216          * Fires when this panel is activated. 
35217          * @param {Roo.ContentPanel} this
35218          */
35219         "deactivate" : true,
35220
35221         /**
35222          * @event resize
35223          * Fires when this panel is resized if fitToFrame is true.
35224          * @param {Roo.ContentPanel} this
35225          * @param {Number} width The width after any component adjustments
35226          * @param {Number} height The height after any component adjustments
35227          */
35228         "resize" : true,
35229         
35230          /**
35231          * @event render
35232          * Fires when this tab is created
35233          * @param {Roo.ContentPanel} this
35234          */
35235         "render" : true
35236         
35237         
35238         
35239     });
35240     
35241
35242     
35243     
35244     if(this.autoScroll){
35245         this.resizeEl.setStyle("overflow", "auto");
35246     } else {
35247         // fix randome scrolling
35248         this.el.on('scroll', function() {
35249             Roo.log('fix random scolling');
35250             this.scrollTo('top',0); 
35251         });
35252     }
35253     content = content || this.content;
35254     if(content){
35255         this.setContent(content);
35256     }
35257     if(config && config.url){
35258         this.setUrl(this.url, this.params, this.loadOnce);
35259     }
35260     
35261     
35262     
35263     Roo.ContentPanel.superclass.constructor.call(this);
35264     
35265     if (this.view && typeof(this.view.xtype) != 'undefined') {
35266         this.view.el = this.el.appendChild(document.createElement("div"));
35267         this.view = Roo.factory(this.view); 
35268         this.view.render  &&  this.view.render(false, '');  
35269     }
35270     
35271     
35272     this.fireEvent('render', this);
35273 };
35274
35275 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
35276     tabTip:'',
35277     setRegion : function(region){
35278         this.region = region;
35279         if(region){
35280            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
35281         }else{
35282            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
35283         } 
35284     },
35285     
35286     /**
35287      * Returns the toolbar for this Panel if one was configured. 
35288      * @return {Roo.Toolbar} 
35289      */
35290     getToolbar : function(){
35291         return this.toolbar;
35292     },
35293     
35294     setActiveState : function(active){
35295         this.active = active;
35296         if(!active){
35297             this.fireEvent("deactivate", this);
35298         }else{
35299             this.fireEvent("activate", this);
35300         }
35301     },
35302     /**
35303      * Updates this panel's element
35304      * @param {String} content The new content
35305      * @param {Boolean} loadScripts (optional) true to look for and process scripts
35306     */
35307     setContent : function(content, loadScripts){
35308         this.el.update(content, loadScripts);
35309     },
35310
35311     ignoreResize : function(w, h){
35312         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
35313             return true;
35314         }else{
35315             this.lastSize = {width: w, height: h};
35316             return false;
35317         }
35318     },
35319     /**
35320      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
35321      * @return {Roo.UpdateManager} The UpdateManager
35322      */
35323     getUpdateManager : function(){
35324         return this.el.getUpdateManager();
35325     },
35326      /**
35327      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
35328      * @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:
35329 <pre><code>
35330 panel.load({
35331     url: "your-url.php",
35332     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
35333     callback: yourFunction,
35334     scope: yourObject, //(optional scope)
35335     discardUrl: false,
35336     nocache: false,
35337     text: "Loading...",
35338     timeout: 30,
35339     scripts: false
35340 });
35341 </code></pre>
35342      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
35343      * 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.
35344      * @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}
35345      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
35346      * @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.
35347      * @return {Roo.ContentPanel} this
35348      */
35349     load : function(){
35350         var um = this.el.getUpdateManager();
35351         um.update.apply(um, arguments);
35352         return this;
35353     },
35354
35355
35356     /**
35357      * 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.
35358      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
35359      * @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)
35360      * @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)
35361      * @return {Roo.UpdateManager} The UpdateManager
35362      */
35363     setUrl : function(url, params, loadOnce){
35364         if(this.refreshDelegate){
35365             this.removeListener("activate", this.refreshDelegate);
35366         }
35367         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35368         this.on("activate", this.refreshDelegate);
35369         return this.el.getUpdateManager();
35370     },
35371     
35372     _handleRefresh : function(url, params, loadOnce){
35373         if(!loadOnce || !this.loaded){
35374             var updater = this.el.getUpdateManager();
35375             updater.update(url, params, this._setLoaded.createDelegate(this));
35376         }
35377     },
35378     
35379     _setLoaded : function(){
35380         this.loaded = true;
35381     }, 
35382     
35383     /**
35384      * Returns this panel's id
35385      * @return {String} 
35386      */
35387     getId : function(){
35388         return this.el.id;
35389     },
35390     
35391     /** 
35392      * Returns this panel's element - used by regiosn to add.
35393      * @return {Roo.Element} 
35394      */
35395     getEl : function(){
35396         return this.wrapEl || this.el;
35397     },
35398     
35399     adjustForComponents : function(width, height)
35400     {
35401         //Roo.log('adjustForComponents ');
35402         if(this.resizeEl != this.el){
35403             width -= this.el.getFrameWidth('lr');
35404             height -= this.el.getFrameWidth('tb');
35405         }
35406         if(this.toolbar){
35407             var te = this.toolbar.getEl();
35408             height -= te.getHeight();
35409             te.setWidth(width);
35410         }
35411         if(this.footer){
35412             var te = this.footer.getEl();
35413             Roo.log("footer:" + te.getHeight());
35414             
35415             height -= te.getHeight();
35416             te.setWidth(width);
35417         }
35418         
35419         
35420         if(this.adjustments){
35421             width += this.adjustments[0];
35422             height += this.adjustments[1];
35423         }
35424         return {"width": width, "height": height};
35425     },
35426     
35427     setSize : function(width, height){
35428         if(this.fitToFrame && !this.ignoreResize(width, height)){
35429             if(this.fitContainer && this.resizeEl != this.el){
35430                 this.el.setSize(width, height);
35431             }
35432             var size = this.adjustForComponents(width, height);
35433             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
35434             this.fireEvent('resize', this, size.width, size.height);
35435         }
35436     },
35437     
35438     /**
35439      * Returns this panel's title
35440      * @return {String} 
35441      */
35442     getTitle : function(){
35443         return this.title;
35444     },
35445     
35446     /**
35447      * Set this panel's title
35448      * @param {String} title
35449      */
35450     setTitle : function(title){
35451         this.title = title;
35452         if(this.region){
35453             this.region.updatePanelTitle(this, title);
35454         }
35455     },
35456     
35457     /**
35458      * Returns true is this panel was configured to be closable
35459      * @return {Boolean} 
35460      */
35461     isClosable : function(){
35462         return this.closable;
35463     },
35464     
35465     beforeSlide : function(){
35466         this.el.clip();
35467         this.resizeEl.clip();
35468     },
35469     
35470     afterSlide : function(){
35471         this.el.unclip();
35472         this.resizeEl.unclip();
35473     },
35474     
35475     /**
35476      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
35477      *   Will fail silently if the {@link #setUrl} method has not been called.
35478      *   This does not activate the panel, just updates its content.
35479      */
35480     refresh : function(){
35481         if(this.refreshDelegate){
35482            this.loaded = false;
35483            this.refreshDelegate();
35484         }
35485     },
35486     
35487     /**
35488      * Destroys this panel
35489      */
35490     destroy : function(){
35491         this.el.removeAllListeners();
35492         var tempEl = document.createElement("span");
35493         tempEl.appendChild(this.el.dom);
35494         tempEl.innerHTML = "";
35495         this.el.remove();
35496         this.el = null;
35497     },
35498     
35499     /**
35500      * form - if the content panel contains a form - this is a reference to it.
35501      * @type {Roo.form.Form}
35502      */
35503     form : false,
35504     /**
35505      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
35506      *    This contains a reference to it.
35507      * @type {Roo.View}
35508      */
35509     view : false,
35510     
35511       /**
35512      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
35513      * <pre><code>
35514
35515 layout.addxtype({
35516        xtype : 'Form',
35517        items: [ .... ]
35518    }
35519 );
35520
35521 </code></pre>
35522      * @param {Object} cfg Xtype definition of item to add.
35523      */
35524     
35525     addxtype : function(cfg) {
35526         // add form..
35527         if (cfg.xtype.match(/^Form$/)) {
35528             
35529             var el;
35530             //if (this.footer) {
35531             //    el = this.footer.container.insertSibling(false, 'before');
35532             //} else {
35533                 el = this.el.createChild();
35534             //}
35535
35536             this.form = new  Roo.form.Form(cfg);
35537             
35538             
35539             if ( this.form.allItems.length) {
35540                 this.form.render(el.dom);
35541             }
35542             return this.form;
35543         }
35544         // should only have one of theses..
35545         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
35546             // views.. should not be just added - used named prop 'view''
35547             
35548             cfg.el = this.el.appendChild(document.createElement("div"));
35549             // factory?
35550             
35551             var ret = new Roo.factory(cfg);
35552              
35553              ret.render && ret.render(false, ''); // render blank..
35554             this.view = ret;
35555             return ret;
35556         }
35557         return false;
35558     }
35559 });
35560
35561 /**
35562  * @class Roo.GridPanel
35563  * @extends Roo.ContentPanel
35564  * @constructor
35565  * Create a new GridPanel.
35566  * @param {Roo.grid.Grid} grid The grid for this panel
35567  * @param {String/Object} config A string to set only the panel's title, or a config object
35568  */
35569 Roo.GridPanel = function(grid, config){
35570     
35571   
35572     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
35573         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
35574         
35575     this.wrapper.dom.appendChild(grid.getGridEl().dom);
35576     
35577     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
35578     
35579     if(this.toolbar){
35580         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
35581     }
35582     // xtype created footer. - not sure if will work as we normally have to render first..
35583     if (this.footer && !this.footer.el && this.footer.xtype) {
35584         
35585         this.footer.container = this.grid.getView().getFooterPanel(true);
35586         this.footer.dataSource = this.grid.dataSource;
35587         this.footer = Roo.factory(this.footer, Roo);
35588         
35589     }
35590     
35591     grid.monitorWindowResize = false; // turn off autosizing
35592     grid.autoHeight = false;
35593     grid.autoWidth = false;
35594     this.grid = grid;
35595     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
35596 };
35597
35598 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
35599     getId : function(){
35600         return this.grid.id;
35601     },
35602     
35603     /**
35604      * Returns the grid for this panel
35605      * @return {Roo.grid.Grid} 
35606      */
35607     getGrid : function(){
35608         return this.grid;    
35609     },
35610     
35611     setSize : function(width, height){
35612         if(!this.ignoreResize(width, height)){
35613             var grid = this.grid;
35614             var size = this.adjustForComponents(width, height);
35615             grid.getGridEl().setSize(size.width, size.height);
35616             grid.autoSize();
35617         }
35618     },
35619     
35620     beforeSlide : function(){
35621         this.grid.getView().scroller.clip();
35622     },
35623     
35624     afterSlide : function(){
35625         this.grid.getView().scroller.unclip();
35626     },
35627     
35628     destroy : function(){
35629         this.grid.destroy();
35630         delete this.grid;
35631         Roo.GridPanel.superclass.destroy.call(this); 
35632     }
35633 });
35634
35635
35636 /**
35637  * @class Roo.NestedLayoutPanel
35638  * @extends Roo.ContentPanel
35639  * @constructor
35640  * Create a new NestedLayoutPanel.
35641  * 
35642  * 
35643  * @param {Roo.BorderLayout} layout The layout for this panel
35644  * @param {String/Object} config A string to set only the title or a config object
35645  */
35646 Roo.NestedLayoutPanel = function(layout, config)
35647 {
35648     // construct with only one argument..
35649     /* FIXME - implement nicer consturctors
35650     if (layout.layout) {
35651         config = layout;
35652         layout = config.layout;
35653         delete config.layout;
35654     }
35655     if (layout.xtype && !layout.getEl) {
35656         // then layout needs constructing..
35657         layout = Roo.factory(layout, Roo);
35658     }
35659     */
35660     
35661     
35662     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
35663     
35664     layout.monitorWindowResize = false; // turn off autosizing
35665     this.layout = layout;
35666     this.layout.getEl().addClass("x-layout-nested-layout");
35667     
35668     
35669     
35670     
35671 };
35672
35673 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
35674
35675     setSize : function(width, height){
35676         if(!this.ignoreResize(width, height)){
35677             var size = this.adjustForComponents(width, height);
35678             var el = this.layout.getEl();
35679             el.setSize(size.width, size.height);
35680             var touch = el.dom.offsetWidth;
35681             this.layout.layout();
35682             // ie requires a double layout on the first pass
35683             if(Roo.isIE && !this.initialized){
35684                 this.initialized = true;
35685                 this.layout.layout();
35686             }
35687         }
35688     },
35689     
35690     // activate all subpanels if not currently active..
35691     
35692     setActiveState : function(active){
35693         this.active = active;
35694         if(!active){
35695             this.fireEvent("deactivate", this);
35696             return;
35697         }
35698         
35699         this.fireEvent("activate", this);
35700         // not sure if this should happen before or after..
35701         if (!this.layout) {
35702             return; // should not happen..
35703         }
35704         var reg = false;
35705         for (var r in this.layout.regions) {
35706             reg = this.layout.getRegion(r);
35707             if (reg.getActivePanel()) {
35708                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
35709                 reg.setActivePanel(reg.getActivePanel());
35710                 continue;
35711             }
35712             if (!reg.panels.length) {
35713                 continue;
35714             }
35715             reg.showPanel(reg.getPanel(0));
35716         }
35717         
35718         
35719         
35720         
35721     },
35722     
35723     /**
35724      * Returns the nested BorderLayout for this panel
35725      * @return {Roo.BorderLayout} 
35726      */
35727     getLayout : function(){
35728         return this.layout;
35729     },
35730     
35731      /**
35732      * Adds a xtype elements to the layout of the nested panel
35733      * <pre><code>
35734
35735 panel.addxtype({
35736        xtype : 'ContentPanel',
35737        region: 'west',
35738        items: [ .... ]
35739    }
35740 );
35741
35742 panel.addxtype({
35743         xtype : 'NestedLayoutPanel',
35744         region: 'west',
35745         layout: {
35746            center: { },
35747            west: { }   
35748         },
35749         items : [ ... list of content panels or nested layout panels.. ]
35750    }
35751 );
35752 </code></pre>
35753      * @param {Object} cfg Xtype definition of item to add.
35754      */
35755     addxtype : function(cfg) {
35756         return this.layout.addxtype(cfg);
35757     
35758     }
35759 });
35760
35761 Roo.ScrollPanel = function(el, config, content){
35762     config = config || {};
35763     config.fitToFrame = true;
35764     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
35765     
35766     this.el.dom.style.overflow = "hidden";
35767     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
35768     this.el.removeClass("x-layout-inactive-content");
35769     this.el.on("mousewheel", this.onWheel, this);
35770
35771     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
35772     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
35773     up.unselectable(); down.unselectable();
35774     up.on("click", this.scrollUp, this);
35775     down.on("click", this.scrollDown, this);
35776     up.addClassOnOver("x-scroller-btn-over");
35777     down.addClassOnOver("x-scroller-btn-over");
35778     up.addClassOnClick("x-scroller-btn-click");
35779     down.addClassOnClick("x-scroller-btn-click");
35780     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
35781
35782     this.resizeEl = this.el;
35783     this.el = wrap; this.up = up; this.down = down;
35784 };
35785
35786 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
35787     increment : 100,
35788     wheelIncrement : 5,
35789     scrollUp : function(){
35790         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
35791     },
35792
35793     scrollDown : function(){
35794         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
35795     },
35796
35797     afterScroll : function(){
35798         var el = this.resizeEl;
35799         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
35800         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35801         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35802     },
35803
35804     setSize : function(){
35805         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
35806         this.afterScroll();
35807     },
35808
35809     onWheel : function(e){
35810         var d = e.getWheelDelta();
35811         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
35812         this.afterScroll();
35813         e.stopEvent();
35814     },
35815
35816     setContent : function(content, loadScripts){
35817         this.resizeEl.update(content, loadScripts);
35818     }
35819
35820 });
35821
35822
35823
35824
35825
35826
35827
35828
35829
35830 /**
35831  * @class Roo.TreePanel
35832  * @extends Roo.ContentPanel
35833  * @constructor
35834  * Create a new TreePanel. - defaults to fit/scoll contents.
35835  * @param {String/Object} config A string to set only the panel's title, or a config object
35836  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
35837  */
35838 Roo.TreePanel = function(config){
35839     var el = config.el;
35840     var tree = config.tree;
35841     delete config.tree; 
35842     delete config.el; // hopefull!
35843     
35844     // wrapper for IE7 strict & safari scroll issue
35845     
35846     var treeEl = el.createChild();
35847     config.resizeEl = treeEl;
35848     
35849     
35850     
35851     Roo.TreePanel.superclass.constructor.call(this, el, config);
35852  
35853  
35854     this.tree = new Roo.tree.TreePanel(treeEl , tree);
35855     //console.log(tree);
35856     this.on('activate', function()
35857     {
35858         if (this.tree.rendered) {
35859             return;
35860         }
35861         //console.log('render tree');
35862         this.tree.render();
35863     });
35864     // this should not be needed.. - it's actually the 'el' that resizes?
35865     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
35866     
35867     //this.on('resize',  function (cp, w, h) {
35868     //        this.tree.innerCt.setWidth(w);
35869     //        this.tree.innerCt.setHeight(h);
35870     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
35871     //});
35872
35873         
35874     
35875 };
35876
35877 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
35878     fitToFrame : true,
35879     autoScroll : true
35880 });
35881
35882
35883
35884
35885
35886
35887
35888
35889
35890
35891
35892 /*
35893  * Based on:
35894  * Ext JS Library 1.1.1
35895  * Copyright(c) 2006-2007, Ext JS, LLC.
35896  *
35897  * Originally Released Under LGPL - original licence link has changed is not relivant.
35898  *
35899  * Fork - LGPL
35900  * <script type="text/javascript">
35901  */
35902  
35903
35904 /**
35905  * @class Roo.ReaderLayout
35906  * @extends Roo.BorderLayout
35907  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
35908  * center region containing two nested regions (a top one for a list view and one for item preview below),
35909  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
35910  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
35911  * expedites the setup of the overall layout and regions for this common application style.
35912  * Example:
35913  <pre><code>
35914 var reader = new Roo.ReaderLayout();
35915 var CP = Roo.ContentPanel;  // shortcut for adding
35916
35917 reader.beginUpdate();
35918 reader.add("north", new CP("north", "North"));
35919 reader.add("west", new CP("west", {title: "West"}));
35920 reader.add("east", new CP("east", {title: "East"}));
35921
35922 reader.regions.listView.add(new CP("listView", "List"));
35923 reader.regions.preview.add(new CP("preview", "Preview"));
35924 reader.endUpdate();
35925 </code></pre>
35926 * @constructor
35927 * Create a new ReaderLayout
35928 * @param {Object} config Configuration options
35929 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
35930 * document.body if omitted)
35931 */
35932 Roo.ReaderLayout = function(config, renderTo){
35933     var c = config || {size:{}};
35934     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
35935         north: c.north !== false ? Roo.apply({
35936             split:false,
35937             initialSize: 32,
35938             titlebar: false
35939         }, c.north) : false,
35940         west: c.west !== false ? Roo.apply({
35941             split:true,
35942             initialSize: 200,
35943             minSize: 175,
35944             maxSize: 400,
35945             titlebar: true,
35946             collapsible: true,
35947             animate: true,
35948             margins:{left:5,right:0,bottom:5,top:5},
35949             cmargins:{left:5,right:5,bottom:5,top:5}
35950         }, c.west) : false,
35951         east: c.east !== false ? Roo.apply({
35952             split:true,
35953             initialSize: 200,
35954             minSize: 175,
35955             maxSize: 400,
35956             titlebar: true,
35957             collapsible: true,
35958             animate: true,
35959             margins:{left:0,right:5,bottom:5,top:5},
35960             cmargins:{left:5,right:5,bottom:5,top:5}
35961         }, c.east) : false,
35962         center: Roo.apply({
35963             tabPosition: 'top',
35964             autoScroll:false,
35965             closeOnTab: true,
35966             titlebar:false,
35967             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
35968         }, c.center)
35969     });
35970
35971     this.el.addClass('x-reader');
35972
35973     this.beginUpdate();
35974
35975     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
35976         south: c.preview !== false ? Roo.apply({
35977             split:true,
35978             initialSize: 200,
35979             minSize: 100,
35980             autoScroll:true,
35981             collapsible:true,
35982             titlebar: true,
35983             cmargins:{top:5,left:0, right:0, bottom:0}
35984         }, c.preview) : false,
35985         center: Roo.apply({
35986             autoScroll:false,
35987             titlebar:false,
35988             minHeight:200
35989         }, c.listView)
35990     });
35991     this.add('center', new Roo.NestedLayoutPanel(inner,
35992             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
35993
35994     this.endUpdate();
35995
35996     this.regions.preview = inner.getRegion('south');
35997     this.regions.listView = inner.getRegion('center');
35998 };
35999
36000 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
36001  * Based on:
36002  * Ext JS Library 1.1.1
36003  * Copyright(c) 2006-2007, Ext JS, LLC.
36004  *
36005  * Originally Released Under LGPL - original licence link has changed is not relivant.
36006  *
36007  * Fork - LGPL
36008  * <script type="text/javascript">
36009  */
36010  
36011 /**
36012  * @class Roo.grid.Grid
36013  * @extends Roo.util.Observable
36014  * This class represents the primary interface of a component based grid control.
36015  * <br><br>Usage:<pre><code>
36016  var grid = new Roo.grid.Grid("my-container-id", {
36017      ds: myDataStore,
36018      cm: myColModel,
36019      selModel: mySelectionModel,
36020      autoSizeColumns: true,
36021      monitorWindowResize: false,
36022      trackMouseOver: true
36023  });
36024  // set any options
36025  grid.render();
36026  * </code></pre>
36027  * <b>Common Problems:</b><br/>
36028  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
36029  * element will correct this<br/>
36030  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
36031  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
36032  * are unpredictable.<br/>
36033  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
36034  * grid to calculate dimensions/offsets.<br/>
36035   * @constructor
36036  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36037  * The container MUST have some type of size defined for the grid to fill. The container will be
36038  * automatically set to position relative if it isn't already.
36039  * @param {Object} config A config object that sets properties on this grid.
36040  */
36041 Roo.grid.Grid = function(container, config){
36042         // initialize the container
36043         this.container = Roo.get(container);
36044         this.container.update("");
36045         this.container.setStyle("overflow", "hidden");
36046     this.container.addClass('x-grid-container');
36047
36048     this.id = this.container.id;
36049
36050     Roo.apply(this, config);
36051     // check and correct shorthanded configs
36052     if(this.ds){
36053         this.dataSource = this.ds;
36054         delete this.ds;
36055     }
36056     if(this.cm){
36057         this.colModel = this.cm;
36058         delete this.cm;
36059     }
36060     if(this.sm){
36061         this.selModel = this.sm;
36062         delete this.sm;
36063     }
36064
36065     if (this.selModel) {
36066         this.selModel = Roo.factory(this.selModel, Roo.grid);
36067         this.sm = this.selModel;
36068         this.sm.xmodule = this.xmodule || false;
36069     }
36070     if (typeof(this.colModel.config) == 'undefined') {
36071         this.colModel = new Roo.grid.ColumnModel(this.colModel);
36072         this.cm = this.colModel;
36073         this.cm.xmodule = this.xmodule || false;
36074     }
36075     if (this.dataSource) {
36076         this.dataSource= Roo.factory(this.dataSource, Roo.data);
36077         this.ds = this.dataSource;
36078         this.ds.xmodule = this.xmodule || false;
36079          
36080     }
36081     
36082     
36083     
36084     if(this.width){
36085         this.container.setWidth(this.width);
36086     }
36087
36088     if(this.height){
36089         this.container.setHeight(this.height);
36090     }
36091     /** @private */
36092         this.addEvents({
36093         // raw events
36094         /**
36095          * @event click
36096          * The raw click event for the entire grid.
36097          * @param {Roo.EventObject} e
36098          */
36099         "click" : true,
36100         /**
36101          * @event dblclick
36102          * The raw dblclick event for the entire grid.
36103          * @param {Roo.EventObject} e
36104          */
36105         "dblclick" : true,
36106         /**
36107          * @event contextmenu
36108          * The raw contextmenu event for the entire grid.
36109          * @param {Roo.EventObject} e
36110          */
36111         "contextmenu" : true,
36112         /**
36113          * @event mousedown
36114          * The raw mousedown event for the entire grid.
36115          * @param {Roo.EventObject} e
36116          */
36117         "mousedown" : true,
36118         /**
36119          * @event mouseup
36120          * The raw mouseup event for the entire grid.
36121          * @param {Roo.EventObject} e
36122          */
36123         "mouseup" : true,
36124         /**
36125          * @event mouseover
36126          * The raw mouseover event for the entire grid.
36127          * @param {Roo.EventObject} e
36128          */
36129         "mouseover" : true,
36130         /**
36131          * @event mouseout
36132          * The raw mouseout event for the entire grid.
36133          * @param {Roo.EventObject} e
36134          */
36135         "mouseout" : true,
36136         /**
36137          * @event keypress
36138          * The raw keypress event for the entire grid.
36139          * @param {Roo.EventObject} e
36140          */
36141         "keypress" : true,
36142         /**
36143          * @event keydown
36144          * The raw keydown event for the entire grid.
36145          * @param {Roo.EventObject} e
36146          */
36147         "keydown" : true,
36148
36149         // custom events
36150
36151         /**
36152          * @event cellclick
36153          * Fires when a cell is clicked
36154          * @param {Grid} this
36155          * @param {Number} rowIndex
36156          * @param {Number} columnIndex
36157          * @param {Roo.EventObject} e
36158          */
36159         "cellclick" : true,
36160         /**
36161          * @event celldblclick
36162          * Fires when a cell is double clicked
36163          * @param {Grid} this
36164          * @param {Number} rowIndex
36165          * @param {Number} columnIndex
36166          * @param {Roo.EventObject} e
36167          */
36168         "celldblclick" : true,
36169         /**
36170          * @event rowclick
36171          * Fires when a row is clicked
36172          * @param {Grid} this
36173          * @param {Number} rowIndex
36174          * @param {Roo.EventObject} e
36175          */
36176         "rowclick" : true,
36177         /**
36178          * @event rowdblclick
36179          * Fires when a row is double clicked
36180          * @param {Grid} this
36181          * @param {Number} rowIndex
36182          * @param {Roo.EventObject} e
36183          */
36184         "rowdblclick" : true,
36185         /**
36186          * @event headerclick
36187          * Fires when a header is clicked
36188          * @param {Grid} this
36189          * @param {Number} columnIndex
36190          * @param {Roo.EventObject} e
36191          */
36192         "headerclick" : true,
36193         /**
36194          * @event headerdblclick
36195          * Fires when a header cell is double clicked
36196          * @param {Grid} this
36197          * @param {Number} columnIndex
36198          * @param {Roo.EventObject} e
36199          */
36200         "headerdblclick" : true,
36201         /**
36202          * @event rowcontextmenu
36203          * Fires when a row is right clicked
36204          * @param {Grid} this
36205          * @param {Number} rowIndex
36206          * @param {Roo.EventObject} e
36207          */
36208         "rowcontextmenu" : true,
36209         /**
36210          * @event cellcontextmenu
36211          * Fires when a cell is right clicked
36212          * @param {Grid} this
36213          * @param {Number} rowIndex
36214          * @param {Number} cellIndex
36215          * @param {Roo.EventObject} e
36216          */
36217          "cellcontextmenu" : true,
36218         /**
36219          * @event headercontextmenu
36220          * Fires when a header is right clicked
36221          * @param {Grid} this
36222          * @param {Number} columnIndex
36223          * @param {Roo.EventObject} e
36224          */
36225         "headercontextmenu" : true,
36226         /**
36227          * @event bodyscroll
36228          * Fires when the body element is scrolled
36229          * @param {Number} scrollLeft
36230          * @param {Number} scrollTop
36231          */
36232         "bodyscroll" : true,
36233         /**
36234          * @event columnresize
36235          * Fires when the user resizes a column
36236          * @param {Number} columnIndex
36237          * @param {Number} newSize
36238          */
36239         "columnresize" : true,
36240         /**
36241          * @event columnmove
36242          * Fires when the user moves a column
36243          * @param {Number} oldIndex
36244          * @param {Number} newIndex
36245          */
36246         "columnmove" : true,
36247         /**
36248          * @event startdrag
36249          * Fires when row(s) start being dragged
36250          * @param {Grid} this
36251          * @param {Roo.GridDD} dd The drag drop object
36252          * @param {event} e The raw browser event
36253          */
36254         "startdrag" : true,
36255         /**
36256          * @event enddrag
36257          * Fires when a drag operation is complete
36258          * @param {Grid} this
36259          * @param {Roo.GridDD} dd The drag drop object
36260          * @param {event} e The raw browser event
36261          */
36262         "enddrag" : true,
36263         /**
36264          * @event dragdrop
36265          * Fires when dragged row(s) are dropped on a valid DD target
36266          * @param {Grid} this
36267          * @param {Roo.GridDD} dd The drag drop object
36268          * @param {String} targetId The target drag drop object
36269          * @param {event} e The raw browser event
36270          */
36271         "dragdrop" : true,
36272         /**
36273          * @event dragover
36274          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
36275          * @param {Grid} this
36276          * @param {Roo.GridDD} dd The drag drop object
36277          * @param {String} targetId The target drag drop object
36278          * @param {event} e The raw browser event
36279          */
36280         "dragover" : true,
36281         /**
36282          * @event dragenter
36283          *  Fires when the dragged row(s) first cross another DD target while being dragged
36284          * @param {Grid} this
36285          * @param {Roo.GridDD} dd The drag drop object
36286          * @param {String} targetId The target drag drop object
36287          * @param {event} e The raw browser event
36288          */
36289         "dragenter" : true,
36290         /**
36291          * @event dragout
36292          * Fires when the dragged row(s) leave another DD target while being dragged
36293          * @param {Grid} this
36294          * @param {Roo.GridDD} dd The drag drop object
36295          * @param {String} targetId The target drag drop object
36296          * @param {event} e The raw browser event
36297          */
36298         "dragout" : true,
36299         /**
36300          * @event rowclass
36301          * Fires when a row is rendered, so you can change add a style to it.
36302          * @param {GridView} gridview   The grid view
36303          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
36304          */
36305         'rowclass' : true,
36306
36307         /**
36308          * @event render
36309          * Fires when the grid is rendered
36310          * @param {Grid} grid
36311          */
36312         'render' : true
36313     });
36314
36315     Roo.grid.Grid.superclass.constructor.call(this);
36316 };
36317 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
36318     
36319     /**
36320      * @cfg {String} ddGroup - drag drop group.
36321      */
36322
36323     /**
36324      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
36325      */
36326     minColumnWidth : 25,
36327
36328     /**
36329      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
36330      * <b>on initial render.</b> It is more efficient to explicitly size the columns
36331      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
36332      */
36333     autoSizeColumns : false,
36334
36335     /**
36336      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
36337      */
36338     autoSizeHeaders : true,
36339
36340     /**
36341      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
36342      */
36343     monitorWindowResize : true,
36344
36345     /**
36346      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
36347      * rows measured to get a columns size. Default is 0 (all rows).
36348      */
36349     maxRowsToMeasure : 0,
36350
36351     /**
36352      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
36353      */
36354     trackMouseOver : true,
36355
36356     /**
36357     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
36358     */
36359     
36360     /**
36361     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
36362     */
36363     enableDragDrop : false,
36364     
36365     /**
36366     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
36367     */
36368     enableColumnMove : true,
36369     
36370     /**
36371     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
36372     */
36373     enableColumnHide : true,
36374     
36375     /**
36376     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
36377     */
36378     enableRowHeightSync : false,
36379     
36380     /**
36381     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
36382     */
36383     stripeRows : true,
36384     
36385     /**
36386     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
36387     */
36388     autoHeight : false,
36389
36390     /**
36391      * @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.
36392      */
36393     autoExpandColumn : false,
36394
36395     /**
36396     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
36397     * Default is 50.
36398     */
36399     autoExpandMin : 50,
36400
36401     /**
36402     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
36403     */
36404     autoExpandMax : 1000,
36405
36406     /**
36407     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
36408     */
36409     view : null,
36410
36411     /**
36412     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
36413     */
36414     loadMask : false,
36415     /**
36416     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
36417     */
36418     dropTarget: false,
36419     
36420    
36421     
36422     // private
36423     rendered : false,
36424
36425     /**
36426     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
36427     * of a fixed width. Default is false.
36428     */
36429     /**
36430     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
36431     */
36432     /**
36433      * Called once after all setup has been completed and the grid is ready to be rendered.
36434      * @return {Roo.grid.Grid} this
36435      */
36436     render : function()
36437     {
36438         var c = this.container;
36439         // try to detect autoHeight/width mode
36440         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
36441             this.autoHeight = true;
36442         }
36443         var view = this.getView();
36444         view.init(this);
36445
36446         c.on("click", this.onClick, this);
36447         c.on("dblclick", this.onDblClick, this);
36448         c.on("contextmenu", this.onContextMenu, this);
36449         c.on("keydown", this.onKeyDown, this);
36450         if (Roo.isTouch) {
36451             c.on("touchstart", this.onTouchStart, this);
36452         }
36453
36454         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
36455
36456         this.getSelectionModel().init(this);
36457
36458         view.render();
36459
36460         if(this.loadMask){
36461             this.loadMask = new Roo.LoadMask(this.container,
36462                     Roo.apply({store:this.dataSource}, this.loadMask));
36463         }
36464         
36465         
36466         if (this.toolbar && this.toolbar.xtype) {
36467             this.toolbar.container = this.getView().getHeaderPanel(true);
36468             this.toolbar = new Roo.Toolbar(this.toolbar);
36469         }
36470         if (this.footer && this.footer.xtype) {
36471             this.footer.dataSource = this.getDataSource();
36472             this.footer.container = this.getView().getFooterPanel(true);
36473             this.footer = Roo.factory(this.footer, Roo);
36474         }
36475         if (this.dropTarget && this.dropTarget.xtype) {
36476             delete this.dropTarget.xtype;
36477             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
36478         }
36479         
36480         
36481         this.rendered = true;
36482         this.fireEvent('render', this);
36483         return this;
36484     },
36485
36486         /**
36487          * Reconfigures the grid to use a different Store and Column Model.
36488          * The View will be bound to the new objects and refreshed.
36489          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
36490          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
36491          */
36492     reconfigure : function(dataSource, colModel){
36493         if(this.loadMask){
36494             this.loadMask.destroy();
36495             this.loadMask = new Roo.LoadMask(this.container,
36496                     Roo.apply({store:dataSource}, this.loadMask));
36497         }
36498         this.view.bind(dataSource, colModel);
36499         this.dataSource = dataSource;
36500         this.colModel = colModel;
36501         this.view.refresh(true);
36502     },
36503
36504     // private
36505     onKeyDown : function(e){
36506         this.fireEvent("keydown", e);
36507     },
36508
36509     /**
36510      * Destroy this grid.
36511      * @param {Boolean} removeEl True to remove the element
36512      */
36513     destroy : function(removeEl, keepListeners){
36514         if(this.loadMask){
36515             this.loadMask.destroy();
36516         }
36517         var c = this.container;
36518         c.removeAllListeners();
36519         this.view.destroy();
36520         this.colModel.purgeListeners();
36521         if(!keepListeners){
36522             this.purgeListeners();
36523         }
36524         c.update("");
36525         if(removeEl === true){
36526             c.remove();
36527         }
36528     },
36529
36530     // private
36531     processEvent : function(name, e){
36532         // does this fire select???
36533         //Roo.log('grid:processEvent '  + name);
36534         
36535         if (name != 'touchstart' ) {
36536             this.fireEvent(name, e);    
36537         }
36538         
36539         var t = e.getTarget();
36540         var v = this.view;
36541         var header = v.findHeaderIndex(t);
36542         if(header !== false){
36543             var ename = name == 'touchstart' ? 'click' : name;
36544              
36545             this.fireEvent("header" + ename, this, header, e);
36546         }else{
36547             var row = v.findRowIndex(t);
36548             var cell = v.findCellIndex(t);
36549             if (name == 'touchstart') {
36550                 // first touch is always a click.
36551                 // hopefull this happens after selection is updated.?
36552                 name = false;
36553                 
36554                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
36555                     var cs = this.selModel.getSelectedCell();
36556                     if (row == cs[0] && cell == cs[1]){
36557                         name = 'dblclick';
36558                     }
36559                 }
36560                 if (typeof(this.selModel.getSelections) != 'undefined') {
36561                     var cs = this.selModel.getSelections();
36562                     var ds = this.dataSource;
36563                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
36564                         name = 'dblclick';
36565                     }
36566                 }
36567                 if (!name) {
36568                     return;
36569                 }
36570             }
36571             
36572             
36573             if(row !== false){
36574                 this.fireEvent("row" + name, this, row, e);
36575                 if(cell !== false){
36576                     this.fireEvent("cell" + name, this, row, cell, e);
36577                 }
36578             }
36579         }
36580     },
36581
36582     // private
36583     onClick : function(e){
36584         this.processEvent("click", e);
36585     },
36586    // private
36587     onTouchStart : function(e){
36588         this.processEvent("touchstart", e);
36589     },
36590
36591     // private
36592     onContextMenu : function(e, t){
36593         this.processEvent("contextmenu", e);
36594     },
36595
36596     // private
36597     onDblClick : function(e){
36598         this.processEvent("dblclick", e);
36599     },
36600
36601     // private
36602     walkCells : function(row, col, step, fn, scope){
36603         var cm = this.colModel, clen = cm.getColumnCount();
36604         var ds = this.dataSource, rlen = ds.getCount(), first = true;
36605         if(step < 0){
36606             if(col < 0){
36607                 row--;
36608                 first = false;
36609             }
36610             while(row >= 0){
36611                 if(!first){
36612                     col = clen-1;
36613                 }
36614                 first = false;
36615                 while(col >= 0){
36616                     if(fn.call(scope || this, row, col, cm) === true){
36617                         return [row, col];
36618                     }
36619                     col--;
36620                 }
36621                 row--;
36622             }
36623         } else {
36624             if(col >= clen){
36625                 row++;
36626                 first = false;
36627             }
36628             while(row < rlen){
36629                 if(!first){
36630                     col = 0;
36631                 }
36632                 first = false;
36633                 while(col < clen){
36634                     if(fn.call(scope || this, row, col, cm) === true){
36635                         return [row, col];
36636                     }
36637                     col++;
36638                 }
36639                 row++;
36640             }
36641         }
36642         return null;
36643     },
36644
36645     // private
36646     getSelections : function(){
36647         return this.selModel.getSelections();
36648     },
36649
36650     /**
36651      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
36652      * but if manual update is required this method will initiate it.
36653      */
36654     autoSize : function(){
36655         if(this.rendered){
36656             this.view.layout();
36657             if(this.view.adjustForScroll){
36658                 this.view.adjustForScroll();
36659             }
36660         }
36661     },
36662
36663     /**
36664      * Returns the grid's underlying element.
36665      * @return {Element} The element
36666      */
36667     getGridEl : function(){
36668         return this.container;
36669     },
36670
36671     // private for compatibility, overridden by editor grid
36672     stopEditing : function(){},
36673
36674     /**
36675      * Returns the grid's SelectionModel.
36676      * @return {SelectionModel}
36677      */
36678     getSelectionModel : function(){
36679         if(!this.selModel){
36680             this.selModel = new Roo.grid.RowSelectionModel();
36681         }
36682         return this.selModel;
36683     },
36684
36685     /**
36686      * Returns the grid's DataSource.
36687      * @return {DataSource}
36688      */
36689     getDataSource : function(){
36690         return this.dataSource;
36691     },
36692
36693     /**
36694      * Returns the grid's ColumnModel.
36695      * @return {ColumnModel}
36696      */
36697     getColumnModel : function(){
36698         return this.colModel;
36699     },
36700
36701     /**
36702      * Returns the grid's GridView object.
36703      * @return {GridView}
36704      */
36705     getView : function(){
36706         if(!this.view){
36707             this.view = new Roo.grid.GridView(this.viewConfig);
36708         }
36709         return this.view;
36710     },
36711     /**
36712      * Called to get grid's drag proxy text, by default returns this.ddText.
36713      * @return {String}
36714      */
36715     getDragDropText : function(){
36716         var count = this.selModel.getCount();
36717         return String.format(this.ddText, count, count == 1 ? '' : 's');
36718     }
36719 });
36720 /**
36721  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
36722  * %0 is replaced with the number of selected rows.
36723  * @type String
36724  */
36725 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
36726  * Based on:
36727  * Ext JS Library 1.1.1
36728  * Copyright(c) 2006-2007, Ext JS, LLC.
36729  *
36730  * Originally Released Under LGPL - original licence link has changed is not relivant.
36731  *
36732  * Fork - LGPL
36733  * <script type="text/javascript">
36734  */
36735  
36736 Roo.grid.AbstractGridView = function(){
36737         this.grid = null;
36738         
36739         this.events = {
36740             "beforerowremoved" : true,
36741             "beforerowsinserted" : true,
36742             "beforerefresh" : true,
36743             "rowremoved" : true,
36744             "rowsinserted" : true,
36745             "rowupdated" : true,
36746             "refresh" : true
36747         };
36748     Roo.grid.AbstractGridView.superclass.constructor.call(this);
36749 };
36750
36751 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
36752     rowClass : "x-grid-row",
36753     cellClass : "x-grid-cell",
36754     tdClass : "x-grid-td",
36755     hdClass : "x-grid-hd",
36756     splitClass : "x-grid-hd-split",
36757     
36758     init: function(grid){
36759         this.grid = grid;
36760                 var cid = this.grid.getGridEl().id;
36761         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
36762         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
36763         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
36764         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
36765         },
36766         
36767     getColumnRenderers : function(){
36768         var renderers = [];
36769         var cm = this.grid.colModel;
36770         var colCount = cm.getColumnCount();
36771         for(var i = 0; i < colCount; i++){
36772             renderers[i] = cm.getRenderer(i);
36773         }
36774         return renderers;
36775     },
36776     
36777     getColumnIds : function(){
36778         var ids = [];
36779         var cm = this.grid.colModel;
36780         var colCount = cm.getColumnCount();
36781         for(var i = 0; i < colCount; i++){
36782             ids[i] = cm.getColumnId(i);
36783         }
36784         return ids;
36785     },
36786     
36787     getDataIndexes : function(){
36788         if(!this.indexMap){
36789             this.indexMap = this.buildIndexMap();
36790         }
36791         return this.indexMap.colToData;
36792     },
36793     
36794     getColumnIndexByDataIndex : function(dataIndex){
36795         if(!this.indexMap){
36796             this.indexMap = this.buildIndexMap();
36797         }
36798         return this.indexMap.dataToCol[dataIndex];
36799     },
36800     
36801     /**
36802      * Set a css style for a column dynamically. 
36803      * @param {Number} colIndex The index of the column
36804      * @param {String} name The css property name
36805      * @param {String} value The css value
36806      */
36807     setCSSStyle : function(colIndex, name, value){
36808         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
36809         Roo.util.CSS.updateRule(selector, name, value);
36810     },
36811     
36812     generateRules : function(cm){
36813         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
36814         Roo.util.CSS.removeStyleSheet(rulesId);
36815         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36816             var cid = cm.getColumnId(i);
36817             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
36818                          this.tdSelector, cid, " {\n}\n",
36819                          this.hdSelector, cid, " {\n}\n",
36820                          this.splitSelector, cid, " {\n}\n");
36821         }
36822         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
36823     }
36824 });/*
36825  * Based on:
36826  * Ext JS Library 1.1.1
36827  * Copyright(c) 2006-2007, Ext JS, LLC.
36828  *
36829  * Originally Released Under LGPL - original licence link has changed is not relivant.
36830  *
36831  * Fork - LGPL
36832  * <script type="text/javascript">
36833  */
36834
36835 // private
36836 // This is a support class used internally by the Grid components
36837 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
36838     this.grid = grid;
36839     this.view = grid.getView();
36840     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36841     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
36842     if(hd2){
36843         this.setHandleElId(Roo.id(hd));
36844         this.setOuterHandleElId(Roo.id(hd2));
36845     }
36846     this.scroll = false;
36847 };
36848 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
36849     maxDragWidth: 120,
36850     getDragData : function(e){
36851         var t = Roo.lib.Event.getTarget(e);
36852         var h = this.view.findHeaderCell(t);
36853         if(h){
36854             return {ddel: h.firstChild, header:h};
36855         }
36856         return false;
36857     },
36858
36859     onInitDrag : function(e){
36860         this.view.headersDisabled = true;
36861         var clone = this.dragData.ddel.cloneNode(true);
36862         clone.id = Roo.id();
36863         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
36864         this.proxy.update(clone);
36865         return true;
36866     },
36867
36868     afterValidDrop : function(){
36869         var v = this.view;
36870         setTimeout(function(){
36871             v.headersDisabled = false;
36872         }, 50);
36873     },
36874
36875     afterInvalidDrop : function(){
36876         var v = this.view;
36877         setTimeout(function(){
36878             v.headersDisabled = false;
36879         }, 50);
36880     }
36881 });
36882 /*
36883  * Based on:
36884  * Ext JS Library 1.1.1
36885  * Copyright(c) 2006-2007, Ext JS, LLC.
36886  *
36887  * Originally Released Under LGPL - original licence link has changed is not relivant.
36888  *
36889  * Fork - LGPL
36890  * <script type="text/javascript">
36891  */
36892 // private
36893 // This is a support class used internally by the Grid components
36894 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
36895     this.grid = grid;
36896     this.view = grid.getView();
36897     // split the proxies so they don't interfere with mouse events
36898     this.proxyTop = Roo.DomHelper.append(document.body, {
36899         cls:"col-move-top", html:"&#160;"
36900     }, true);
36901     this.proxyBottom = Roo.DomHelper.append(document.body, {
36902         cls:"col-move-bottom", html:"&#160;"
36903     }, true);
36904     this.proxyTop.hide = this.proxyBottom.hide = function(){
36905         this.setLeftTop(-100,-100);
36906         this.setStyle("visibility", "hidden");
36907     };
36908     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36909     // temporarily disabled
36910     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
36911     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
36912 };
36913 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
36914     proxyOffsets : [-4, -9],
36915     fly: Roo.Element.fly,
36916
36917     getTargetFromEvent : function(e){
36918         var t = Roo.lib.Event.getTarget(e);
36919         var cindex = this.view.findCellIndex(t);
36920         if(cindex !== false){
36921             return this.view.getHeaderCell(cindex);
36922         }
36923         return null;
36924     },
36925
36926     nextVisible : function(h){
36927         var v = this.view, cm = this.grid.colModel;
36928         h = h.nextSibling;
36929         while(h){
36930             if(!cm.isHidden(v.getCellIndex(h))){
36931                 return h;
36932             }
36933             h = h.nextSibling;
36934         }
36935         return null;
36936     },
36937
36938     prevVisible : function(h){
36939         var v = this.view, cm = this.grid.colModel;
36940         h = h.prevSibling;
36941         while(h){
36942             if(!cm.isHidden(v.getCellIndex(h))){
36943                 return h;
36944             }
36945             h = h.prevSibling;
36946         }
36947         return null;
36948     },
36949
36950     positionIndicator : function(h, n, e){
36951         var x = Roo.lib.Event.getPageX(e);
36952         var r = Roo.lib.Dom.getRegion(n.firstChild);
36953         var px, pt, py = r.top + this.proxyOffsets[1];
36954         if((r.right - x) <= (r.right-r.left)/2){
36955             px = r.right+this.view.borderWidth;
36956             pt = "after";
36957         }else{
36958             px = r.left;
36959             pt = "before";
36960         }
36961         var oldIndex = this.view.getCellIndex(h);
36962         var newIndex = this.view.getCellIndex(n);
36963
36964         if(this.grid.colModel.isFixed(newIndex)){
36965             return false;
36966         }
36967
36968         var locked = this.grid.colModel.isLocked(newIndex);
36969
36970         if(pt == "after"){
36971             newIndex++;
36972         }
36973         if(oldIndex < newIndex){
36974             newIndex--;
36975         }
36976         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
36977             return false;
36978         }
36979         px +=  this.proxyOffsets[0];
36980         this.proxyTop.setLeftTop(px, py);
36981         this.proxyTop.show();
36982         if(!this.bottomOffset){
36983             this.bottomOffset = this.view.mainHd.getHeight();
36984         }
36985         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
36986         this.proxyBottom.show();
36987         return pt;
36988     },
36989
36990     onNodeEnter : function(n, dd, e, data){
36991         if(data.header != n){
36992             this.positionIndicator(data.header, n, e);
36993         }
36994     },
36995
36996     onNodeOver : function(n, dd, e, data){
36997         var result = false;
36998         if(data.header != n){
36999             result = this.positionIndicator(data.header, n, e);
37000         }
37001         if(!result){
37002             this.proxyTop.hide();
37003             this.proxyBottom.hide();
37004         }
37005         return result ? this.dropAllowed : this.dropNotAllowed;
37006     },
37007
37008     onNodeOut : function(n, dd, e, data){
37009         this.proxyTop.hide();
37010         this.proxyBottom.hide();
37011     },
37012
37013     onNodeDrop : function(n, dd, e, data){
37014         var h = data.header;
37015         if(h != n){
37016             var cm = this.grid.colModel;
37017             var x = Roo.lib.Event.getPageX(e);
37018             var r = Roo.lib.Dom.getRegion(n.firstChild);
37019             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
37020             var oldIndex = this.view.getCellIndex(h);
37021             var newIndex = this.view.getCellIndex(n);
37022             var locked = cm.isLocked(newIndex);
37023             if(pt == "after"){
37024                 newIndex++;
37025             }
37026             if(oldIndex < newIndex){
37027                 newIndex--;
37028             }
37029             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
37030                 return false;
37031             }
37032             cm.setLocked(oldIndex, locked, true);
37033             cm.moveColumn(oldIndex, newIndex);
37034             this.grid.fireEvent("columnmove", oldIndex, newIndex);
37035             return true;
37036         }
37037         return false;
37038     }
37039 });
37040 /*
37041  * Based on:
37042  * Ext JS Library 1.1.1
37043  * Copyright(c) 2006-2007, Ext JS, LLC.
37044  *
37045  * Originally Released Under LGPL - original licence link has changed is not relivant.
37046  *
37047  * Fork - LGPL
37048  * <script type="text/javascript">
37049  */
37050   
37051 /**
37052  * @class Roo.grid.GridView
37053  * @extends Roo.util.Observable
37054  *
37055  * @constructor
37056  * @param {Object} config
37057  */
37058 Roo.grid.GridView = function(config){
37059     Roo.grid.GridView.superclass.constructor.call(this);
37060     this.el = null;
37061
37062     Roo.apply(this, config);
37063 };
37064
37065 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
37066
37067     unselectable :  'unselectable="on"',
37068     unselectableCls :  'x-unselectable',
37069     
37070     
37071     rowClass : "x-grid-row",
37072
37073     cellClass : "x-grid-col",
37074
37075     tdClass : "x-grid-td",
37076
37077     hdClass : "x-grid-hd",
37078
37079     splitClass : "x-grid-split",
37080
37081     sortClasses : ["sort-asc", "sort-desc"],
37082
37083     enableMoveAnim : false,
37084
37085     hlColor: "C3DAF9",
37086
37087     dh : Roo.DomHelper,
37088
37089     fly : Roo.Element.fly,
37090
37091     css : Roo.util.CSS,
37092
37093     borderWidth: 1,
37094
37095     splitOffset: 3,
37096
37097     scrollIncrement : 22,
37098
37099     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
37100
37101     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
37102
37103     bind : function(ds, cm){
37104         if(this.ds){
37105             this.ds.un("load", this.onLoad, this);
37106             this.ds.un("datachanged", this.onDataChange, this);
37107             this.ds.un("add", this.onAdd, this);
37108             this.ds.un("remove", this.onRemove, this);
37109             this.ds.un("update", this.onUpdate, this);
37110             this.ds.un("clear", this.onClear, this);
37111         }
37112         if(ds){
37113             ds.on("load", this.onLoad, this);
37114             ds.on("datachanged", this.onDataChange, this);
37115             ds.on("add", this.onAdd, this);
37116             ds.on("remove", this.onRemove, this);
37117             ds.on("update", this.onUpdate, this);
37118             ds.on("clear", this.onClear, this);
37119         }
37120         this.ds = ds;
37121
37122         if(this.cm){
37123             this.cm.un("widthchange", this.onColWidthChange, this);
37124             this.cm.un("headerchange", this.onHeaderChange, this);
37125             this.cm.un("hiddenchange", this.onHiddenChange, this);
37126             this.cm.un("columnmoved", this.onColumnMove, this);
37127             this.cm.un("columnlockchange", this.onColumnLock, this);
37128         }
37129         if(cm){
37130             this.generateRules(cm);
37131             cm.on("widthchange", this.onColWidthChange, this);
37132             cm.on("headerchange", this.onHeaderChange, this);
37133             cm.on("hiddenchange", this.onHiddenChange, this);
37134             cm.on("columnmoved", this.onColumnMove, this);
37135             cm.on("columnlockchange", this.onColumnLock, this);
37136         }
37137         this.cm = cm;
37138     },
37139
37140     init: function(grid){
37141         Roo.grid.GridView.superclass.init.call(this, grid);
37142
37143         this.bind(grid.dataSource, grid.colModel);
37144
37145         grid.on("headerclick", this.handleHeaderClick, this);
37146
37147         if(grid.trackMouseOver){
37148             grid.on("mouseover", this.onRowOver, this);
37149             grid.on("mouseout", this.onRowOut, this);
37150         }
37151         grid.cancelTextSelection = function(){};
37152         this.gridId = grid.id;
37153
37154         var tpls = this.templates || {};
37155
37156         if(!tpls.master){
37157             tpls.master = new Roo.Template(
37158                '<div class="x-grid" hidefocus="true">',
37159                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
37160                   '<div class="x-grid-topbar"></div>',
37161                   '<div class="x-grid-scroller"><div></div></div>',
37162                   '<div class="x-grid-locked">',
37163                       '<div class="x-grid-header">{lockedHeader}</div>',
37164                       '<div class="x-grid-body">{lockedBody}</div>',
37165                   "</div>",
37166                   '<div class="x-grid-viewport">',
37167                       '<div class="x-grid-header">{header}</div>',
37168                       '<div class="x-grid-body">{body}</div>',
37169                   "</div>",
37170                   '<div class="x-grid-bottombar"></div>',
37171                  
37172                   '<div class="x-grid-resize-proxy">&#160;</div>',
37173                "</div>"
37174             );
37175             tpls.master.disableformats = true;
37176         }
37177
37178         if(!tpls.header){
37179             tpls.header = new Roo.Template(
37180                '<table border="0" cellspacing="0" cellpadding="0">',
37181                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
37182                "</table>{splits}"
37183             );
37184             tpls.header.disableformats = true;
37185         }
37186         tpls.header.compile();
37187
37188         if(!tpls.hcell){
37189             tpls.hcell = new Roo.Template(
37190                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
37191                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
37192                 "</div></td>"
37193              );
37194              tpls.hcell.disableFormats = true;
37195         }
37196         tpls.hcell.compile();
37197
37198         if(!tpls.hsplit){
37199             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
37200                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
37201             tpls.hsplit.disableFormats = true;
37202         }
37203         tpls.hsplit.compile();
37204
37205         if(!tpls.body){
37206             tpls.body = new Roo.Template(
37207                '<table border="0" cellspacing="0" cellpadding="0">',
37208                "<tbody>{rows}</tbody>",
37209                "</table>"
37210             );
37211             tpls.body.disableFormats = true;
37212         }
37213         tpls.body.compile();
37214
37215         if(!tpls.row){
37216             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
37217             tpls.row.disableFormats = true;
37218         }
37219         tpls.row.compile();
37220
37221         if(!tpls.cell){
37222             tpls.cell = new Roo.Template(
37223                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
37224                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
37225                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
37226                 "</td>"
37227             );
37228             tpls.cell.disableFormats = true;
37229         }
37230         tpls.cell.compile();
37231
37232         this.templates = tpls;
37233     },
37234
37235     // remap these for backwards compat
37236     onColWidthChange : function(){
37237         this.updateColumns.apply(this, arguments);
37238     },
37239     onHeaderChange : function(){
37240         this.updateHeaders.apply(this, arguments);
37241     }, 
37242     onHiddenChange : function(){
37243         this.handleHiddenChange.apply(this, arguments);
37244     },
37245     onColumnMove : function(){
37246         this.handleColumnMove.apply(this, arguments);
37247     },
37248     onColumnLock : function(){
37249         this.handleLockChange.apply(this, arguments);
37250     },
37251
37252     onDataChange : function(){
37253         this.refresh();
37254         this.updateHeaderSortState();
37255     },
37256
37257     onClear : function(){
37258         this.refresh();
37259     },
37260
37261     onUpdate : function(ds, record){
37262         this.refreshRow(record);
37263     },
37264
37265     refreshRow : function(record){
37266         var ds = this.ds, index;
37267         if(typeof record == 'number'){
37268             index = record;
37269             record = ds.getAt(index);
37270         }else{
37271             index = ds.indexOf(record);
37272         }
37273         this.insertRows(ds, index, index, true);
37274         this.onRemove(ds, record, index+1, true);
37275         this.syncRowHeights(index, index);
37276         this.layout();
37277         this.fireEvent("rowupdated", this, index, record);
37278     },
37279
37280     onAdd : function(ds, records, index){
37281         this.insertRows(ds, index, index + (records.length-1));
37282     },
37283
37284     onRemove : function(ds, record, index, isUpdate){
37285         if(isUpdate !== true){
37286             this.fireEvent("beforerowremoved", this, index, record);
37287         }
37288         var bt = this.getBodyTable(), lt = this.getLockedTable();
37289         if(bt.rows[index]){
37290             bt.firstChild.removeChild(bt.rows[index]);
37291         }
37292         if(lt.rows[index]){
37293             lt.firstChild.removeChild(lt.rows[index]);
37294         }
37295         if(isUpdate !== true){
37296             this.stripeRows(index);
37297             this.syncRowHeights(index, index);
37298             this.layout();
37299             this.fireEvent("rowremoved", this, index, record);
37300         }
37301     },
37302
37303     onLoad : function(){
37304         this.scrollToTop();
37305     },
37306
37307     /**
37308      * Scrolls the grid to the top
37309      */
37310     scrollToTop : function(){
37311         if(this.scroller){
37312             this.scroller.dom.scrollTop = 0;
37313             this.syncScroll();
37314         }
37315     },
37316
37317     /**
37318      * Gets a panel in the header of the grid that can be used for toolbars etc.
37319      * After modifying the contents of this panel a call to grid.autoSize() may be
37320      * required to register any changes in size.
37321      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
37322      * @return Roo.Element
37323      */
37324     getHeaderPanel : function(doShow){
37325         if(doShow){
37326             this.headerPanel.show();
37327         }
37328         return this.headerPanel;
37329     },
37330
37331     /**
37332      * Gets a panel in the footer of the grid that can be used for toolbars etc.
37333      * After modifying the contents of this panel a call to grid.autoSize() may be
37334      * required to register any changes in size.
37335      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
37336      * @return Roo.Element
37337      */
37338     getFooterPanel : function(doShow){
37339         if(doShow){
37340             this.footerPanel.show();
37341         }
37342         return this.footerPanel;
37343     },
37344
37345     initElements : function(){
37346         var E = Roo.Element;
37347         var el = this.grid.getGridEl().dom.firstChild;
37348         var cs = el.childNodes;
37349
37350         this.el = new E(el);
37351         
37352          this.focusEl = new E(el.firstChild);
37353         this.focusEl.swallowEvent("click", true);
37354         
37355         this.headerPanel = new E(cs[1]);
37356         this.headerPanel.enableDisplayMode("block");
37357
37358         this.scroller = new E(cs[2]);
37359         this.scrollSizer = new E(this.scroller.dom.firstChild);
37360
37361         this.lockedWrap = new E(cs[3]);
37362         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
37363         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
37364
37365         this.mainWrap = new E(cs[4]);
37366         this.mainHd = new E(this.mainWrap.dom.firstChild);
37367         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
37368
37369         this.footerPanel = new E(cs[5]);
37370         this.footerPanel.enableDisplayMode("block");
37371
37372         this.resizeProxy = new E(cs[6]);
37373
37374         this.headerSelector = String.format(
37375            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
37376            this.lockedHd.id, this.mainHd.id
37377         );
37378
37379         this.splitterSelector = String.format(
37380            '#{0} div.x-grid-split, #{1} div.x-grid-split',
37381            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
37382         );
37383     },
37384     idToCssName : function(s)
37385     {
37386         return s.replace(/[^a-z0-9]+/ig, '-');
37387     },
37388
37389     getHeaderCell : function(index){
37390         return Roo.DomQuery.select(this.headerSelector)[index];
37391     },
37392
37393     getHeaderCellMeasure : function(index){
37394         return this.getHeaderCell(index).firstChild;
37395     },
37396
37397     getHeaderCellText : function(index){
37398         return this.getHeaderCell(index).firstChild.firstChild;
37399     },
37400
37401     getLockedTable : function(){
37402         return this.lockedBody.dom.firstChild;
37403     },
37404
37405     getBodyTable : function(){
37406         return this.mainBody.dom.firstChild;
37407     },
37408
37409     getLockedRow : function(index){
37410         return this.getLockedTable().rows[index];
37411     },
37412
37413     getRow : function(index){
37414         return this.getBodyTable().rows[index];
37415     },
37416
37417     getRowComposite : function(index){
37418         if(!this.rowEl){
37419             this.rowEl = new Roo.CompositeElementLite();
37420         }
37421         var els = [], lrow, mrow;
37422         if(lrow = this.getLockedRow(index)){
37423             els.push(lrow);
37424         }
37425         if(mrow = this.getRow(index)){
37426             els.push(mrow);
37427         }
37428         this.rowEl.elements = els;
37429         return this.rowEl;
37430     },
37431     /**
37432      * Gets the 'td' of the cell
37433      * 
37434      * @param {Integer} rowIndex row to select
37435      * @param {Integer} colIndex column to select
37436      * 
37437      * @return {Object} 
37438      */
37439     getCell : function(rowIndex, colIndex){
37440         var locked = this.cm.getLockedCount();
37441         var source;
37442         if(colIndex < locked){
37443             source = this.lockedBody.dom.firstChild;
37444         }else{
37445             source = this.mainBody.dom.firstChild;
37446             colIndex -= locked;
37447         }
37448         return source.rows[rowIndex].childNodes[colIndex];
37449     },
37450
37451     getCellText : function(rowIndex, colIndex){
37452         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
37453     },
37454
37455     getCellBox : function(cell){
37456         var b = this.fly(cell).getBox();
37457         if(Roo.isOpera){ // opera fails to report the Y
37458             b.y = cell.offsetTop + this.mainBody.getY();
37459         }
37460         return b;
37461     },
37462
37463     getCellIndex : function(cell){
37464         var id = String(cell.className).match(this.cellRE);
37465         if(id){
37466             return parseInt(id[1], 10);
37467         }
37468         return 0;
37469     },
37470
37471     findHeaderIndex : function(n){
37472         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
37473         return r ? this.getCellIndex(r) : false;
37474     },
37475
37476     findHeaderCell : function(n){
37477         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
37478         return r ? r : false;
37479     },
37480
37481     findRowIndex : function(n){
37482         if(!n){
37483             return false;
37484         }
37485         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
37486         return r ? r.rowIndex : false;
37487     },
37488
37489     findCellIndex : function(node){
37490         var stop = this.el.dom;
37491         while(node && node != stop){
37492             if(this.findRE.test(node.className)){
37493                 return this.getCellIndex(node);
37494             }
37495             node = node.parentNode;
37496         }
37497         return false;
37498     },
37499
37500     getColumnId : function(index){
37501         return this.cm.getColumnId(index);
37502     },
37503
37504     getSplitters : function()
37505     {
37506         if(this.splitterSelector){
37507            return Roo.DomQuery.select(this.splitterSelector);
37508         }else{
37509             return null;
37510       }
37511     },
37512
37513     getSplitter : function(index){
37514         return this.getSplitters()[index];
37515     },
37516
37517     onRowOver : function(e, t){
37518         var row;
37519         if((row = this.findRowIndex(t)) !== false){
37520             this.getRowComposite(row).addClass("x-grid-row-over");
37521         }
37522     },
37523
37524     onRowOut : function(e, t){
37525         var row;
37526         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
37527             this.getRowComposite(row).removeClass("x-grid-row-over");
37528         }
37529     },
37530
37531     renderHeaders : function(){
37532         var cm = this.cm;
37533         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
37534         var cb = [], lb = [], sb = [], lsb = [], p = {};
37535         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37536             p.cellId = "x-grid-hd-0-" + i;
37537             p.splitId = "x-grid-csplit-0-" + i;
37538             p.id = cm.getColumnId(i);
37539             p.value = cm.getColumnHeader(i) || "";
37540             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
37541             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
37542             if(!cm.isLocked(i)){
37543                 cb[cb.length] = ct.apply(p);
37544                 sb[sb.length] = st.apply(p);
37545             }else{
37546                 lb[lb.length] = ct.apply(p);
37547                 lsb[lsb.length] = st.apply(p);
37548             }
37549         }
37550         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
37551                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
37552     },
37553
37554     updateHeaders : function(){
37555         var html = this.renderHeaders();
37556         this.lockedHd.update(html[0]);
37557         this.mainHd.update(html[1]);
37558     },
37559
37560     /**
37561      * Focuses the specified row.
37562      * @param {Number} row The row index
37563      */
37564     focusRow : function(row)
37565     {
37566         //Roo.log('GridView.focusRow');
37567         var x = this.scroller.dom.scrollLeft;
37568         this.focusCell(row, 0, false);
37569         this.scroller.dom.scrollLeft = x;
37570     },
37571
37572     /**
37573      * Focuses the specified cell.
37574      * @param {Number} row The row index
37575      * @param {Number} col The column index
37576      * @param {Boolean} hscroll false to disable horizontal scrolling
37577      */
37578     focusCell : function(row, col, hscroll)
37579     {
37580         //Roo.log('GridView.focusCell');
37581         var el = this.ensureVisible(row, col, hscroll);
37582         this.focusEl.alignTo(el, "tl-tl");
37583         if(Roo.isGecko){
37584             this.focusEl.focus();
37585         }else{
37586             this.focusEl.focus.defer(1, this.focusEl);
37587         }
37588     },
37589
37590     /**
37591      * Scrolls the specified cell into view
37592      * @param {Number} row The row index
37593      * @param {Number} col The column index
37594      * @param {Boolean} hscroll false to disable horizontal scrolling
37595      */
37596     ensureVisible : function(row, col, hscroll)
37597     {
37598         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
37599         //return null; //disable for testing.
37600         if(typeof row != "number"){
37601             row = row.rowIndex;
37602         }
37603         if(row < 0 && row >= this.ds.getCount()){
37604             return  null;
37605         }
37606         col = (col !== undefined ? col : 0);
37607         var cm = this.grid.colModel;
37608         while(cm.isHidden(col)){
37609             col++;
37610         }
37611
37612         var el = this.getCell(row, col);
37613         if(!el){
37614             return null;
37615         }
37616         var c = this.scroller.dom;
37617
37618         var ctop = parseInt(el.offsetTop, 10);
37619         var cleft = parseInt(el.offsetLeft, 10);
37620         var cbot = ctop + el.offsetHeight;
37621         var cright = cleft + el.offsetWidth;
37622         
37623         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
37624         var stop = parseInt(c.scrollTop, 10);
37625         var sleft = parseInt(c.scrollLeft, 10);
37626         var sbot = stop + ch;
37627         var sright = sleft + c.clientWidth;
37628         /*
37629         Roo.log('GridView.ensureVisible:' +
37630                 ' ctop:' + ctop +
37631                 ' c.clientHeight:' + c.clientHeight +
37632                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
37633                 ' stop:' + stop +
37634                 ' cbot:' + cbot +
37635                 ' sbot:' + sbot +
37636                 ' ch:' + ch  
37637                 );
37638         */
37639         if(ctop < stop){
37640              c.scrollTop = ctop;
37641             //Roo.log("set scrolltop to ctop DISABLE?");
37642         }else if(cbot > sbot){
37643             //Roo.log("set scrolltop to cbot-ch");
37644             c.scrollTop = cbot-ch;
37645         }
37646         
37647         if(hscroll !== false){
37648             if(cleft < sleft){
37649                 c.scrollLeft = cleft;
37650             }else if(cright > sright){
37651                 c.scrollLeft = cright-c.clientWidth;
37652             }
37653         }
37654          
37655         return el;
37656     },
37657
37658     updateColumns : function(){
37659         this.grid.stopEditing();
37660         var cm = this.grid.colModel, colIds = this.getColumnIds();
37661         //var totalWidth = cm.getTotalWidth();
37662         var pos = 0;
37663         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37664             //if(cm.isHidden(i)) continue;
37665             var w = cm.getColumnWidth(i);
37666             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37667             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37668         }
37669         this.updateSplitters();
37670     },
37671
37672     generateRules : function(cm){
37673         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
37674         Roo.util.CSS.removeStyleSheet(rulesId);
37675         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37676             var cid = cm.getColumnId(i);
37677             var align = '';
37678             if(cm.config[i].align){
37679                 align = 'text-align:'+cm.config[i].align+';';
37680             }
37681             var hidden = '';
37682             if(cm.isHidden(i)){
37683                 hidden = 'display:none;';
37684             }
37685             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
37686             ruleBuf.push(
37687                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
37688                     this.hdSelector, cid, " {\n", align, width, "}\n",
37689                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
37690                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
37691         }
37692         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37693     },
37694
37695     updateSplitters : function(){
37696         var cm = this.cm, s = this.getSplitters();
37697         if(s){ // splitters not created yet
37698             var pos = 0, locked = true;
37699             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37700                 if(cm.isHidden(i)) {
37701                     continue;
37702                 }
37703                 var w = cm.getColumnWidth(i); // make sure it's a number
37704                 if(!cm.isLocked(i) && locked){
37705                     pos = 0;
37706                     locked = false;
37707                 }
37708                 pos += w;
37709                 s[i].style.left = (pos-this.splitOffset) + "px";
37710             }
37711         }
37712     },
37713
37714     handleHiddenChange : function(colModel, colIndex, hidden){
37715         if(hidden){
37716             this.hideColumn(colIndex);
37717         }else{
37718             this.unhideColumn(colIndex);
37719         }
37720     },
37721
37722     hideColumn : function(colIndex){
37723         var cid = this.getColumnId(colIndex);
37724         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
37725         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
37726         if(Roo.isSafari){
37727             this.updateHeaders();
37728         }
37729         this.updateSplitters();
37730         this.layout();
37731     },
37732
37733     unhideColumn : function(colIndex){
37734         var cid = this.getColumnId(colIndex);
37735         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
37736         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
37737
37738         if(Roo.isSafari){
37739             this.updateHeaders();
37740         }
37741         this.updateSplitters();
37742         this.layout();
37743     },
37744
37745     insertRows : function(dm, firstRow, lastRow, isUpdate){
37746         if(firstRow == 0 && lastRow == dm.getCount()-1){
37747             this.refresh();
37748         }else{
37749             if(!isUpdate){
37750                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
37751             }
37752             var s = this.getScrollState();
37753             var markup = this.renderRows(firstRow, lastRow);
37754             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
37755             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
37756             this.restoreScroll(s);
37757             if(!isUpdate){
37758                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
37759                 this.syncRowHeights(firstRow, lastRow);
37760                 this.stripeRows(firstRow);
37761                 this.layout();
37762             }
37763         }
37764     },
37765
37766     bufferRows : function(markup, target, index){
37767         var before = null, trows = target.rows, tbody = target.tBodies[0];
37768         if(index < trows.length){
37769             before = trows[index];
37770         }
37771         var b = document.createElement("div");
37772         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
37773         var rows = b.firstChild.rows;
37774         for(var i = 0, len = rows.length; i < len; i++){
37775             if(before){
37776                 tbody.insertBefore(rows[0], before);
37777             }else{
37778                 tbody.appendChild(rows[0]);
37779             }
37780         }
37781         b.innerHTML = "";
37782         b = null;
37783     },
37784
37785     deleteRows : function(dm, firstRow, lastRow){
37786         if(dm.getRowCount()<1){
37787             this.fireEvent("beforerefresh", this);
37788             this.mainBody.update("");
37789             this.lockedBody.update("");
37790             this.fireEvent("refresh", this);
37791         }else{
37792             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
37793             var bt = this.getBodyTable();
37794             var tbody = bt.firstChild;
37795             var rows = bt.rows;
37796             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
37797                 tbody.removeChild(rows[firstRow]);
37798             }
37799             this.stripeRows(firstRow);
37800             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
37801         }
37802     },
37803
37804     updateRows : function(dataSource, firstRow, lastRow){
37805         var s = this.getScrollState();
37806         this.refresh();
37807         this.restoreScroll(s);
37808     },
37809
37810     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
37811         if(!noRefresh){
37812            this.refresh();
37813         }
37814         this.updateHeaderSortState();
37815     },
37816
37817     getScrollState : function(){
37818         
37819         var sb = this.scroller.dom;
37820         return {left: sb.scrollLeft, top: sb.scrollTop};
37821     },
37822
37823     stripeRows : function(startRow){
37824         if(!this.grid.stripeRows || this.ds.getCount() < 1){
37825             return;
37826         }
37827         startRow = startRow || 0;
37828         var rows = this.getBodyTable().rows;
37829         var lrows = this.getLockedTable().rows;
37830         var cls = ' x-grid-row-alt ';
37831         for(var i = startRow, len = rows.length; i < len; i++){
37832             var row = rows[i], lrow = lrows[i];
37833             var isAlt = ((i+1) % 2 == 0);
37834             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
37835             if(isAlt == hasAlt){
37836                 continue;
37837             }
37838             if(isAlt){
37839                 row.className += " x-grid-row-alt";
37840             }else{
37841                 row.className = row.className.replace("x-grid-row-alt", "");
37842             }
37843             if(lrow){
37844                 lrow.className = row.className;
37845             }
37846         }
37847     },
37848
37849     restoreScroll : function(state){
37850         //Roo.log('GridView.restoreScroll');
37851         var sb = this.scroller.dom;
37852         sb.scrollLeft = state.left;
37853         sb.scrollTop = state.top;
37854         this.syncScroll();
37855     },
37856
37857     syncScroll : function(){
37858         //Roo.log('GridView.syncScroll');
37859         var sb = this.scroller.dom;
37860         var sh = this.mainHd.dom;
37861         var bs = this.mainBody.dom;
37862         var lv = this.lockedBody.dom;
37863         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
37864         lv.scrollTop = bs.scrollTop = sb.scrollTop;
37865     },
37866
37867     handleScroll : function(e){
37868         this.syncScroll();
37869         var sb = this.scroller.dom;
37870         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
37871         e.stopEvent();
37872     },
37873
37874     handleWheel : function(e){
37875         var d = e.getWheelDelta();
37876         this.scroller.dom.scrollTop -= d*22;
37877         // set this here to prevent jumpy scrolling on large tables
37878         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
37879         e.stopEvent();
37880     },
37881
37882     renderRows : function(startRow, endRow){
37883         // pull in all the crap needed to render rows
37884         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
37885         var colCount = cm.getColumnCount();
37886
37887         if(ds.getCount() < 1){
37888             return ["", ""];
37889         }
37890
37891         // build a map for all the columns
37892         var cs = [];
37893         for(var i = 0; i < colCount; i++){
37894             var name = cm.getDataIndex(i);
37895             cs[i] = {
37896                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
37897                 renderer : cm.getRenderer(i),
37898                 id : cm.getColumnId(i),
37899                 locked : cm.isLocked(i),
37900                 has_editor : cm.isCellEditable(i)
37901             };
37902         }
37903
37904         startRow = startRow || 0;
37905         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
37906
37907         // records to render
37908         var rs = ds.getRange(startRow, endRow);
37909
37910         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
37911     },
37912
37913     // As much as I hate to duplicate code, this was branched because FireFox really hates
37914     // [].join("") on strings. The performance difference was substantial enough to
37915     // branch this function
37916     doRender : Roo.isGecko ?
37917             function(cs, rs, ds, startRow, colCount, stripe){
37918                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37919                 // buffers
37920                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37921                 
37922                 var hasListener = this.grid.hasListener('rowclass');
37923                 var rowcfg = {};
37924                 for(var j = 0, len = rs.length; j < len; j++){
37925                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
37926                     for(var i = 0; i < colCount; i++){
37927                         c = cs[i];
37928                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37929                         p.id = c.id;
37930                         p.css = p.attr = "";
37931                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37932                         if(p.value == undefined || p.value === "") {
37933                             p.value = "&#160;";
37934                         }
37935                         if(c.has_editor){
37936                             p.css += ' x-grid-editable-cell';
37937                         }
37938                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
37939                             p.css +=  ' x-grid-dirty-cell';
37940                         }
37941                         var markup = ct.apply(p);
37942                         if(!c.locked){
37943                             cb+= markup;
37944                         }else{
37945                             lcb+= markup;
37946                         }
37947                     }
37948                     var alt = [];
37949                     if(stripe && ((rowIndex+1) % 2 == 0)){
37950                         alt.push("x-grid-row-alt")
37951                     }
37952                     if(r.dirty){
37953                         alt.push(  " x-grid-dirty-row");
37954                     }
37955                     rp.cells = lcb;
37956                     if(this.getRowClass){
37957                         alt.push(this.getRowClass(r, rowIndex));
37958                     }
37959                     if (hasListener) {
37960                         rowcfg = {
37961                              
37962                             record: r,
37963                             rowIndex : rowIndex,
37964                             rowClass : ''
37965                         };
37966                         this.grid.fireEvent('rowclass', this, rowcfg);
37967                         alt.push(rowcfg.rowClass);
37968                     }
37969                     rp.alt = alt.join(" ");
37970                     lbuf+= rt.apply(rp);
37971                     rp.cells = cb;
37972                     buf+=  rt.apply(rp);
37973                 }
37974                 return [lbuf, buf];
37975             } :
37976             function(cs, rs, ds, startRow, colCount, stripe){
37977                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37978                 // buffers
37979                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37980                 var hasListener = this.grid.hasListener('rowclass');
37981  
37982                 var rowcfg = {};
37983                 for(var j = 0, len = rs.length; j < len; j++){
37984                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
37985                     for(var i = 0; i < colCount; i++){
37986                         c = cs[i];
37987                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37988                         p.id = c.id;
37989                         p.css = p.attr = "";
37990                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37991                         if(p.value == undefined || p.value === "") {
37992                             p.value = "&#160;";
37993                         }
37994                         //Roo.log(c);
37995                          if(c.has_editor){
37996                             p.css += ' x-grid-editable-cell';
37997                         }
37998                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37999                             p.css += ' x-grid-dirty-cell' 
38000                         }
38001                         
38002                         var markup = ct.apply(p);
38003                         if(!c.locked){
38004                             cb[cb.length] = markup;
38005                         }else{
38006                             lcb[lcb.length] = markup;
38007                         }
38008                     }
38009                     var alt = [];
38010                     if(stripe && ((rowIndex+1) % 2 == 0)){
38011                         alt.push( "x-grid-row-alt");
38012                     }
38013                     if(r.dirty){
38014                         alt.push(" x-grid-dirty-row");
38015                     }
38016                     rp.cells = lcb;
38017                     if(this.getRowClass){
38018                         alt.push( this.getRowClass(r, rowIndex));
38019                     }
38020                     if (hasListener) {
38021                         rowcfg = {
38022                              
38023                             record: r,
38024                             rowIndex : rowIndex,
38025                             rowClass : ''
38026                         };
38027                         this.grid.fireEvent('rowclass', this, rowcfg);
38028                         alt.push(rowcfg.rowClass);
38029                     }
38030                     
38031                     rp.alt = alt.join(" ");
38032                     rp.cells = lcb.join("");
38033                     lbuf[lbuf.length] = rt.apply(rp);
38034                     rp.cells = cb.join("");
38035                     buf[buf.length] =  rt.apply(rp);
38036                 }
38037                 return [lbuf.join(""), buf.join("")];
38038             },
38039
38040     renderBody : function(){
38041         var markup = this.renderRows();
38042         var bt = this.templates.body;
38043         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
38044     },
38045
38046     /**
38047      * Refreshes the grid
38048      * @param {Boolean} headersToo
38049      */
38050     refresh : function(headersToo){
38051         this.fireEvent("beforerefresh", this);
38052         this.grid.stopEditing();
38053         var result = this.renderBody();
38054         this.lockedBody.update(result[0]);
38055         this.mainBody.update(result[1]);
38056         if(headersToo === true){
38057             this.updateHeaders();
38058             this.updateColumns();
38059             this.updateSplitters();
38060             this.updateHeaderSortState();
38061         }
38062         this.syncRowHeights();
38063         this.layout();
38064         this.fireEvent("refresh", this);
38065     },
38066
38067     handleColumnMove : function(cm, oldIndex, newIndex){
38068         this.indexMap = null;
38069         var s = this.getScrollState();
38070         this.refresh(true);
38071         this.restoreScroll(s);
38072         this.afterMove(newIndex);
38073     },
38074
38075     afterMove : function(colIndex){
38076         if(this.enableMoveAnim && Roo.enableFx){
38077             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
38078         }
38079         // if multisort - fix sortOrder, and reload..
38080         if (this.grid.dataSource.multiSort) {
38081             // the we can call sort again..
38082             var dm = this.grid.dataSource;
38083             var cm = this.grid.colModel;
38084             var so = [];
38085             for(var i = 0; i < cm.config.length; i++ ) {
38086                 
38087                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
38088                     continue; // dont' bother, it's not in sort list or being set.
38089                 }
38090                 
38091                 so.push(cm.config[i].dataIndex);
38092             };
38093             dm.sortOrder = so;
38094             dm.load(dm.lastOptions);
38095             
38096             
38097         }
38098         
38099     },
38100
38101     updateCell : function(dm, rowIndex, dataIndex){
38102         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
38103         if(typeof colIndex == "undefined"){ // not present in grid
38104             return;
38105         }
38106         var cm = this.grid.colModel;
38107         var cell = this.getCell(rowIndex, colIndex);
38108         var cellText = this.getCellText(rowIndex, colIndex);
38109
38110         var p = {
38111             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
38112             id : cm.getColumnId(colIndex),
38113             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
38114         };
38115         var renderer = cm.getRenderer(colIndex);
38116         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
38117         if(typeof val == "undefined" || val === "") {
38118             val = "&#160;";
38119         }
38120         cellText.innerHTML = val;
38121         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
38122         this.syncRowHeights(rowIndex, rowIndex);
38123     },
38124
38125     calcColumnWidth : function(colIndex, maxRowsToMeasure){
38126         var maxWidth = 0;
38127         if(this.grid.autoSizeHeaders){
38128             var h = this.getHeaderCellMeasure(colIndex);
38129             maxWidth = Math.max(maxWidth, h.scrollWidth);
38130         }
38131         var tb, index;
38132         if(this.cm.isLocked(colIndex)){
38133             tb = this.getLockedTable();
38134             index = colIndex;
38135         }else{
38136             tb = this.getBodyTable();
38137             index = colIndex - this.cm.getLockedCount();
38138         }
38139         if(tb && tb.rows){
38140             var rows = tb.rows;
38141             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
38142             for(var i = 0; i < stopIndex; i++){
38143                 var cell = rows[i].childNodes[index].firstChild;
38144                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
38145             }
38146         }
38147         return maxWidth + /*margin for error in IE*/ 5;
38148     },
38149     /**
38150      * Autofit a column to its content.
38151      * @param {Number} colIndex
38152      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
38153      */
38154      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
38155          if(this.cm.isHidden(colIndex)){
38156              return; // can't calc a hidden column
38157          }
38158         if(forceMinSize){
38159             var cid = this.cm.getColumnId(colIndex);
38160             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
38161            if(this.grid.autoSizeHeaders){
38162                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
38163            }
38164         }
38165         var newWidth = this.calcColumnWidth(colIndex);
38166         this.cm.setColumnWidth(colIndex,
38167             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
38168         if(!suppressEvent){
38169             this.grid.fireEvent("columnresize", colIndex, newWidth);
38170         }
38171     },
38172
38173     /**
38174      * Autofits all columns to their content and then expands to fit any extra space in the grid
38175      */
38176      autoSizeColumns : function(){
38177         var cm = this.grid.colModel;
38178         var colCount = cm.getColumnCount();
38179         for(var i = 0; i < colCount; i++){
38180             this.autoSizeColumn(i, true, true);
38181         }
38182         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
38183             this.fitColumns();
38184         }else{
38185             this.updateColumns();
38186             this.layout();
38187         }
38188     },
38189
38190     /**
38191      * Autofits all columns to the grid's width proportionate with their current size
38192      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
38193      */
38194     fitColumns : function(reserveScrollSpace){
38195         var cm = this.grid.colModel;
38196         var colCount = cm.getColumnCount();
38197         var cols = [];
38198         var width = 0;
38199         var i, w;
38200         for (i = 0; i < colCount; i++){
38201             if(!cm.isHidden(i) && !cm.isFixed(i)){
38202                 w = cm.getColumnWidth(i);
38203                 cols.push(i);
38204                 cols.push(w);
38205                 width += w;
38206             }
38207         }
38208         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
38209         if(reserveScrollSpace){
38210             avail -= 17;
38211         }
38212         var frac = (avail - cm.getTotalWidth())/width;
38213         while (cols.length){
38214             w = cols.pop();
38215             i = cols.pop();
38216             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
38217         }
38218         this.updateColumns();
38219         this.layout();
38220     },
38221
38222     onRowSelect : function(rowIndex){
38223         var row = this.getRowComposite(rowIndex);
38224         row.addClass("x-grid-row-selected");
38225     },
38226
38227     onRowDeselect : function(rowIndex){
38228         var row = this.getRowComposite(rowIndex);
38229         row.removeClass("x-grid-row-selected");
38230     },
38231
38232     onCellSelect : function(row, col){
38233         var cell = this.getCell(row, col);
38234         if(cell){
38235             Roo.fly(cell).addClass("x-grid-cell-selected");
38236         }
38237     },
38238
38239     onCellDeselect : function(row, col){
38240         var cell = this.getCell(row, col);
38241         if(cell){
38242             Roo.fly(cell).removeClass("x-grid-cell-selected");
38243         }
38244     },
38245
38246     updateHeaderSortState : function(){
38247         
38248         // sort state can be single { field: xxx, direction : yyy}
38249         // or   { xxx=>ASC , yyy : DESC ..... }
38250         
38251         var mstate = {};
38252         if (!this.ds.multiSort) { 
38253             var state = this.ds.getSortState();
38254             if(!state){
38255                 return;
38256             }
38257             mstate[state.field] = state.direction;
38258             // FIXME... - this is not used here.. but might be elsewhere..
38259             this.sortState = state;
38260             
38261         } else {
38262             mstate = this.ds.sortToggle;
38263         }
38264         //remove existing sort classes..
38265         
38266         var sc = this.sortClasses;
38267         var hds = this.el.select(this.headerSelector).removeClass(sc);
38268         
38269         for(var f in mstate) {
38270         
38271             var sortColumn = this.cm.findColumnIndex(f);
38272             
38273             if(sortColumn != -1){
38274                 var sortDir = mstate[f];        
38275                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
38276             }
38277         }
38278         
38279          
38280         
38281     },
38282
38283
38284     handleHeaderClick : function(g, index,e){
38285         
38286         Roo.log("header click");
38287         
38288         if (Roo.isTouch) {
38289             // touch events on header are handled by context
38290             this.handleHdCtx(g,index,e);
38291             return;
38292         }
38293         
38294         
38295         if(this.headersDisabled){
38296             return;
38297         }
38298         var dm = g.dataSource, cm = g.colModel;
38299         if(!cm.isSortable(index)){
38300             return;
38301         }
38302         g.stopEditing();
38303         
38304         if (dm.multiSort) {
38305             // update the sortOrder
38306             var so = [];
38307             for(var i = 0; i < cm.config.length; i++ ) {
38308                 
38309                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
38310                     continue; // dont' bother, it's not in sort list or being set.
38311                 }
38312                 
38313                 so.push(cm.config[i].dataIndex);
38314             };
38315             dm.sortOrder = so;
38316         }
38317         
38318         
38319         dm.sort(cm.getDataIndex(index));
38320     },
38321
38322
38323     destroy : function(){
38324         if(this.colMenu){
38325             this.colMenu.removeAll();
38326             Roo.menu.MenuMgr.unregister(this.colMenu);
38327             this.colMenu.getEl().remove();
38328             delete this.colMenu;
38329         }
38330         if(this.hmenu){
38331             this.hmenu.removeAll();
38332             Roo.menu.MenuMgr.unregister(this.hmenu);
38333             this.hmenu.getEl().remove();
38334             delete this.hmenu;
38335         }
38336         if(this.grid.enableColumnMove){
38337             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
38338             if(dds){
38339                 for(var dd in dds){
38340                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
38341                         var elid = dds[dd].dragElId;
38342                         dds[dd].unreg();
38343                         Roo.get(elid).remove();
38344                     } else if(dds[dd].config.isTarget){
38345                         dds[dd].proxyTop.remove();
38346                         dds[dd].proxyBottom.remove();
38347                         dds[dd].unreg();
38348                     }
38349                     if(Roo.dd.DDM.locationCache[dd]){
38350                         delete Roo.dd.DDM.locationCache[dd];
38351                     }
38352                 }
38353                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
38354             }
38355         }
38356         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
38357         this.bind(null, null);
38358         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
38359     },
38360
38361     handleLockChange : function(){
38362         this.refresh(true);
38363     },
38364
38365     onDenyColumnLock : function(){
38366
38367     },
38368
38369     onDenyColumnHide : function(){
38370
38371     },
38372
38373     handleHdMenuClick : function(item){
38374         var index = this.hdCtxIndex;
38375         var cm = this.cm, ds = this.ds;
38376         switch(item.id){
38377             case "asc":
38378                 ds.sort(cm.getDataIndex(index), "ASC");
38379                 break;
38380             case "desc":
38381                 ds.sort(cm.getDataIndex(index), "DESC");
38382                 break;
38383             case "lock":
38384                 var lc = cm.getLockedCount();
38385                 if(cm.getColumnCount(true) <= lc+1){
38386                     this.onDenyColumnLock();
38387                     return;
38388                 }
38389                 if(lc != index){
38390                     cm.setLocked(index, true, true);
38391                     cm.moveColumn(index, lc);
38392                     this.grid.fireEvent("columnmove", index, lc);
38393                 }else{
38394                     cm.setLocked(index, true);
38395                 }
38396             break;
38397             case "unlock":
38398                 var lc = cm.getLockedCount();
38399                 if((lc-1) != index){
38400                     cm.setLocked(index, false, true);
38401                     cm.moveColumn(index, lc-1);
38402                     this.grid.fireEvent("columnmove", index, lc-1);
38403                 }else{
38404                     cm.setLocked(index, false);
38405                 }
38406             break;
38407             case 'wider': // used to expand cols on touch..
38408             case 'narrow':
38409                 var cw = cm.getColumnWidth(index);
38410                 cw += (item.id == 'wider' ? 1 : -1) * 50;
38411                 cw = Math.max(0, cw);
38412                 cw = Math.min(cw,4000);
38413                 cm.setColumnWidth(index, cw);
38414                 break;
38415                 
38416             default:
38417                 index = cm.getIndexById(item.id.substr(4));
38418                 if(index != -1){
38419                     if(item.checked && cm.getColumnCount(true) <= 1){
38420                         this.onDenyColumnHide();
38421                         return false;
38422                     }
38423                     cm.setHidden(index, item.checked);
38424                 }
38425         }
38426         return true;
38427     },
38428
38429     beforeColMenuShow : function(){
38430         var cm = this.cm,  colCount = cm.getColumnCount();
38431         this.colMenu.removeAll();
38432         for(var i = 0; i < colCount; i++){
38433             this.colMenu.add(new Roo.menu.CheckItem({
38434                 id: "col-"+cm.getColumnId(i),
38435                 text: cm.getColumnHeader(i),
38436                 checked: !cm.isHidden(i),
38437                 hideOnClick:false
38438             }));
38439         }
38440     },
38441
38442     handleHdCtx : function(g, index, e){
38443         e.stopEvent();
38444         var hd = this.getHeaderCell(index);
38445         this.hdCtxIndex = index;
38446         var ms = this.hmenu.items, cm = this.cm;
38447         ms.get("asc").setDisabled(!cm.isSortable(index));
38448         ms.get("desc").setDisabled(!cm.isSortable(index));
38449         if(this.grid.enableColLock !== false){
38450             ms.get("lock").setDisabled(cm.isLocked(index));
38451             ms.get("unlock").setDisabled(!cm.isLocked(index));
38452         }
38453         this.hmenu.show(hd, "tl-bl");
38454     },
38455
38456     handleHdOver : function(e){
38457         var hd = this.findHeaderCell(e.getTarget());
38458         if(hd && !this.headersDisabled){
38459             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
38460                this.fly(hd).addClass("x-grid-hd-over");
38461             }
38462         }
38463     },
38464
38465     handleHdOut : function(e){
38466         var hd = this.findHeaderCell(e.getTarget());
38467         if(hd){
38468             this.fly(hd).removeClass("x-grid-hd-over");
38469         }
38470     },
38471
38472     handleSplitDblClick : function(e, t){
38473         var i = this.getCellIndex(t);
38474         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
38475             this.autoSizeColumn(i, true);
38476             this.layout();
38477         }
38478     },
38479
38480     render : function(){
38481
38482         var cm = this.cm;
38483         var colCount = cm.getColumnCount();
38484
38485         if(this.grid.monitorWindowResize === true){
38486             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38487         }
38488         var header = this.renderHeaders();
38489         var body = this.templates.body.apply({rows:""});
38490         var html = this.templates.master.apply({
38491             lockedBody: body,
38492             body: body,
38493             lockedHeader: header[0],
38494             header: header[1]
38495         });
38496
38497         //this.updateColumns();
38498
38499         this.grid.getGridEl().dom.innerHTML = html;
38500
38501         this.initElements();
38502         
38503         // a kludge to fix the random scolling effect in webkit
38504         this.el.on("scroll", function() {
38505             this.el.dom.scrollTop=0; // hopefully not recursive..
38506         },this);
38507
38508         this.scroller.on("scroll", this.handleScroll, this);
38509         this.lockedBody.on("mousewheel", this.handleWheel, this);
38510         this.mainBody.on("mousewheel", this.handleWheel, this);
38511
38512         this.mainHd.on("mouseover", this.handleHdOver, this);
38513         this.mainHd.on("mouseout", this.handleHdOut, this);
38514         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
38515                 {delegate: "."+this.splitClass});
38516
38517         this.lockedHd.on("mouseover", this.handleHdOver, this);
38518         this.lockedHd.on("mouseout", this.handleHdOut, this);
38519         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
38520                 {delegate: "."+this.splitClass});
38521
38522         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
38523             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38524         }
38525
38526         this.updateSplitters();
38527
38528         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
38529             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38530             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38531         }
38532
38533         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
38534             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
38535             this.hmenu.add(
38536                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
38537                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
38538             );
38539             if(this.grid.enableColLock !== false){
38540                 this.hmenu.add('-',
38541                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
38542                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
38543                 );
38544             }
38545             if (Roo.isTouch) {
38546                  this.hmenu.add('-',
38547                     {id:"wider", text: this.columnsWiderText},
38548                     {id:"narrow", text: this.columnsNarrowText }
38549                 );
38550                 
38551                  
38552             }
38553             
38554             if(this.grid.enableColumnHide !== false){
38555
38556                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
38557                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
38558                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
38559
38560                 this.hmenu.add('-',
38561                     {id:"columns", text: this.columnsText, menu: this.colMenu}
38562                 );
38563             }
38564             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
38565
38566             this.grid.on("headercontextmenu", this.handleHdCtx, this);
38567         }
38568
38569         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
38570             this.dd = new Roo.grid.GridDragZone(this.grid, {
38571                 ddGroup : this.grid.ddGroup || 'GridDD'
38572             });
38573             
38574         }
38575
38576         /*
38577         for(var i = 0; i < colCount; i++){
38578             if(cm.isHidden(i)){
38579                 this.hideColumn(i);
38580             }
38581             if(cm.config[i].align){
38582                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
38583                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
38584             }
38585         }*/
38586         
38587         this.updateHeaderSortState();
38588
38589         this.beforeInitialResize();
38590         this.layout(true);
38591
38592         // two part rendering gives faster view to the user
38593         this.renderPhase2.defer(1, this);
38594     },
38595
38596     renderPhase2 : function(){
38597         // render the rows now
38598         this.refresh();
38599         if(this.grid.autoSizeColumns){
38600             this.autoSizeColumns();
38601         }
38602     },
38603
38604     beforeInitialResize : function(){
38605
38606     },
38607
38608     onColumnSplitterMoved : function(i, w){
38609         this.userResized = true;
38610         var cm = this.grid.colModel;
38611         cm.setColumnWidth(i, w, true);
38612         var cid = cm.getColumnId(i);
38613         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
38614         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
38615         this.updateSplitters();
38616         this.layout();
38617         this.grid.fireEvent("columnresize", i, w);
38618     },
38619
38620     syncRowHeights : function(startIndex, endIndex){
38621         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
38622             startIndex = startIndex || 0;
38623             var mrows = this.getBodyTable().rows;
38624             var lrows = this.getLockedTable().rows;
38625             var len = mrows.length-1;
38626             endIndex = Math.min(endIndex || len, len);
38627             for(var i = startIndex; i <= endIndex; i++){
38628                 var m = mrows[i], l = lrows[i];
38629                 var h = Math.max(m.offsetHeight, l.offsetHeight);
38630                 m.style.height = l.style.height = h + "px";
38631             }
38632         }
38633     },
38634
38635     layout : function(initialRender, is2ndPass){
38636         var g = this.grid;
38637         var auto = g.autoHeight;
38638         var scrollOffset = 16;
38639         var c = g.getGridEl(), cm = this.cm,
38640                 expandCol = g.autoExpandColumn,
38641                 gv = this;
38642         //c.beginMeasure();
38643
38644         if(!c.dom.offsetWidth){ // display:none?
38645             if(initialRender){
38646                 this.lockedWrap.show();
38647                 this.mainWrap.show();
38648             }
38649             return;
38650         }
38651
38652         var hasLock = this.cm.isLocked(0);
38653
38654         var tbh = this.headerPanel.getHeight();
38655         var bbh = this.footerPanel.getHeight();
38656
38657         if(auto){
38658             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
38659             var newHeight = ch + c.getBorderWidth("tb");
38660             if(g.maxHeight){
38661                 newHeight = Math.min(g.maxHeight, newHeight);
38662             }
38663             c.setHeight(newHeight);
38664         }
38665
38666         if(g.autoWidth){
38667             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
38668         }
38669
38670         var s = this.scroller;
38671
38672         var csize = c.getSize(true);
38673
38674         this.el.setSize(csize.width, csize.height);
38675
38676         this.headerPanel.setWidth(csize.width);
38677         this.footerPanel.setWidth(csize.width);
38678
38679         var hdHeight = this.mainHd.getHeight();
38680         var vw = csize.width;
38681         var vh = csize.height - (tbh + bbh);
38682
38683         s.setSize(vw, vh);
38684
38685         var bt = this.getBodyTable();
38686         
38687         if(cm.getLockedCount() == cm.config.length){
38688             bt = this.getLockedTable();
38689         }
38690         
38691         var ltWidth = hasLock ?
38692                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
38693
38694         var scrollHeight = bt.offsetHeight;
38695         var scrollWidth = ltWidth + bt.offsetWidth;
38696         var vscroll = false, hscroll = false;
38697
38698         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
38699
38700         var lw = this.lockedWrap, mw = this.mainWrap;
38701         var lb = this.lockedBody, mb = this.mainBody;
38702
38703         setTimeout(function(){
38704             var t = s.dom.offsetTop;
38705             var w = s.dom.clientWidth,
38706                 h = s.dom.clientHeight;
38707
38708             lw.setTop(t);
38709             lw.setSize(ltWidth, h);
38710
38711             mw.setLeftTop(ltWidth, t);
38712             mw.setSize(w-ltWidth, h);
38713
38714             lb.setHeight(h-hdHeight);
38715             mb.setHeight(h-hdHeight);
38716
38717             if(is2ndPass !== true && !gv.userResized && expandCol){
38718                 // high speed resize without full column calculation
38719                 
38720                 var ci = cm.getIndexById(expandCol);
38721                 if (ci < 0) {
38722                     ci = cm.findColumnIndex(expandCol);
38723                 }
38724                 ci = Math.max(0, ci); // make sure it's got at least the first col.
38725                 var expandId = cm.getColumnId(ci);
38726                 var  tw = cm.getTotalWidth(false);
38727                 var currentWidth = cm.getColumnWidth(ci);
38728                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
38729                 if(currentWidth != cw){
38730                     cm.setColumnWidth(ci, cw, true);
38731                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38732                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38733                     gv.updateSplitters();
38734                     gv.layout(false, true);
38735                 }
38736             }
38737
38738             if(initialRender){
38739                 lw.show();
38740                 mw.show();
38741             }
38742             //c.endMeasure();
38743         }, 10);
38744     },
38745
38746     onWindowResize : function(){
38747         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
38748             return;
38749         }
38750         this.layout();
38751     },
38752
38753     appendFooter : function(parentEl){
38754         return null;
38755     },
38756
38757     sortAscText : "Sort Ascending",
38758     sortDescText : "Sort Descending",
38759     lockText : "Lock Column",
38760     unlockText : "Unlock Column",
38761     columnsText : "Columns",
38762  
38763     columnsWiderText : "Wider",
38764     columnsNarrowText : "Thinner"
38765 });
38766
38767
38768 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
38769     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
38770     this.proxy.el.addClass('x-grid3-col-dd');
38771 };
38772
38773 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
38774     handleMouseDown : function(e){
38775
38776     },
38777
38778     callHandleMouseDown : function(e){
38779         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
38780     }
38781 });
38782 /*
38783  * Based on:
38784  * Ext JS Library 1.1.1
38785  * Copyright(c) 2006-2007, Ext JS, LLC.
38786  *
38787  * Originally Released Under LGPL - original licence link has changed is not relivant.
38788  *
38789  * Fork - LGPL
38790  * <script type="text/javascript">
38791  */
38792  
38793 // private
38794 // This is a support class used internally by the Grid components
38795 Roo.grid.SplitDragZone = function(grid, hd, hd2){
38796     this.grid = grid;
38797     this.view = grid.getView();
38798     this.proxy = this.view.resizeProxy;
38799     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
38800         "gridSplitters" + this.grid.getGridEl().id, {
38801         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
38802     });
38803     this.setHandleElId(Roo.id(hd));
38804     this.setOuterHandleElId(Roo.id(hd2));
38805     this.scroll = false;
38806 };
38807 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
38808     fly: Roo.Element.fly,
38809
38810     b4StartDrag : function(x, y){
38811         this.view.headersDisabled = true;
38812         this.proxy.setHeight(this.view.mainWrap.getHeight());
38813         var w = this.cm.getColumnWidth(this.cellIndex);
38814         var minw = Math.max(w-this.grid.minColumnWidth, 0);
38815         this.resetConstraints();
38816         this.setXConstraint(minw, 1000);
38817         this.setYConstraint(0, 0);
38818         this.minX = x - minw;
38819         this.maxX = x + 1000;
38820         this.startPos = x;
38821         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
38822     },
38823
38824
38825     handleMouseDown : function(e){
38826         ev = Roo.EventObject.setEvent(e);
38827         var t = this.fly(ev.getTarget());
38828         if(t.hasClass("x-grid-split")){
38829             this.cellIndex = this.view.getCellIndex(t.dom);
38830             this.split = t.dom;
38831             this.cm = this.grid.colModel;
38832             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
38833                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
38834             }
38835         }
38836     },
38837
38838     endDrag : function(e){
38839         this.view.headersDisabled = false;
38840         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
38841         var diff = endX - this.startPos;
38842         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
38843     },
38844
38845     autoOffset : function(){
38846         this.setDelta(0,0);
38847     }
38848 });/*
38849  * Based on:
38850  * Ext JS Library 1.1.1
38851  * Copyright(c) 2006-2007, Ext JS, LLC.
38852  *
38853  * Originally Released Under LGPL - original licence link has changed is not relivant.
38854  *
38855  * Fork - LGPL
38856  * <script type="text/javascript">
38857  */
38858  
38859 // private
38860 // This is a support class used internally by the Grid components
38861 Roo.grid.GridDragZone = function(grid, config){
38862     this.view = grid.getView();
38863     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
38864     if(this.view.lockedBody){
38865         this.setHandleElId(Roo.id(this.view.mainBody.dom));
38866         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
38867     }
38868     this.scroll = false;
38869     this.grid = grid;
38870     this.ddel = document.createElement('div');
38871     this.ddel.className = 'x-grid-dd-wrap';
38872 };
38873
38874 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
38875     ddGroup : "GridDD",
38876
38877     getDragData : function(e){
38878         var t = Roo.lib.Event.getTarget(e);
38879         var rowIndex = this.view.findRowIndex(t);
38880         var sm = this.grid.selModel;
38881             
38882         //Roo.log(rowIndex);
38883         
38884         if (sm.getSelectedCell) {
38885             // cell selection..
38886             if (!sm.getSelectedCell()) {
38887                 return false;
38888             }
38889             if (rowIndex != sm.getSelectedCell()[0]) {
38890                 return false;
38891             }
38892         
38893         }
38894         
38895         if(rowIndex !== false){
38896             
38897             // if editorgrid.. 
38898             
38899             
38900             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
38901                
38902             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
38903               //  
38904             //}
38905             if (e.hasModifier()){
38906                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
38907             }
38908             
38909             Roo.log("getDragData");
38910             
38911             return {
38912                 grid: this.grid,
38913                 ddel: this.ddel,
38914                 rowIndex: rowIndex,
38915                 selections:sm.getSelections ? sm.getSelections() : (
38916                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
38917                 )
38918             };
38919         }
38920         return false;
38921     },
38922
38923     onInitDrag : function(e){
38924         var data = this.dragData;
38925         this.ddel.innerHTML = this.grid.getDragDropText();
38926         this.proxy.update(this.ddel);
38927         // fire start drag?
38928     },
38929
38930     afterRepair : function(){
38931         this.dragging = false;
38932     },
38933
38934     getRepairXY : function(e, data){
38935         return false;
38936     },
38937
38938     onEndDrag : function(data, e){
38939         // fire end drag?
38940     },
38941
38942     onValidDrop : function(dd, e, id){
38943         // fire drag drop?
38944         this.hideProxy();
38945     },
38946
38947     beforeInvalidDrop : function(e, id){
38948
38949     }
38950 });/*
38951  * Based on:
38952  * Ext JS Library 1.1.1
38953  * Copyright(c) 2006-2007, Ext JS, LLC.
38954  *
38955  * Originally Released Under LGPL - original licence link has changed is not relivant.
38956  *
38957  * Fork - LGPL
38958  * <script type="text/javascript">
38959  */
38960  
38961
38962 /**
38963  * @class Roo.grid.ColumnModel
38964  * @extends Roo.util.Observable
38965  * This is the default implementation of a ColumnModel used by the Grid. It defines
38966  * the columns in the grid.
38967  * <br>Usage:<br>
38968  <pre><code>
38969  var colModel = new Roo.grid.ColumnModel([
38970         {header: "Ticker", width: 60, sortable: true, locked: true},
38971         {header: "Company Name", width: 150, sortable: true},
38972         {header: "Market Cap.", width: 100, sortable: true},
38973         {header: "$ Sales", width: 100, sortable: true, renderer: money},
38974         {header: "Employees", width: 100, sortable: true, resizable: false}
38975  ]);
38976  </code></pre>
38977  * <p>
38978  
38979  * The config options listed for this class are options which may appear in each
38980  * individual column definition.
38981  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
38982  * @constructor
38983  * @param {Object} config An Array of column config objects. See this class's
38984  * config objects for details.
38985 */
38986 Roo.grid.ColumnModel = function(config){
38987         /**
38988      * The config passed into the constructor
38989      */
38990     this.config = config;
38991     this.lookup = {};
38992
38993     // if no id, create one
38994     // if the column does not have a dataIndex mapping,
38995     // map it to the order it is in the config
38996     for(var i = 0, len = config.length; i < len; i++){
38997         var c = config[i];
38998         if(typeof c.dataIndex == "undefined"){
38999             c.dataIndex = i;
39000         }
39001         if(typeof c.renderer == "string"){
39002             c.renderer = Roo.util.Format[c.renderer];
39003         }
39004         if(typeof c.id == "undefined"){
39005             c.id = Roo.id();
39006         }
39007         if(c.editor && c.editor.xtype){
39008             c.editor  = Roo.factory(c.editor, Roo.grid);
39009         }
39010         if(c.editor && c.editor.isFormField){
39011             c.editor = new Roo.grid.GridEditor(c.editor);
39012         }
39013         this.lookup[c.id] = c;
39014     }
39015
39016     /**
39017      * The width of columns which have no width specified (defaults to 100)
39018      * @type Number
39019      */
39020     this.defaultWidth = 100;
39021
39022     /**
39023      * Default sortable of columns which have no sortable specified (defaults to false)
39024      * @type Boolean
39025      */
39026     this.defaultSortable = false;
39027
39028     this.addEvents({
39029         /**
39030              * @event widthchange
39031              * Fires when the width of a column changes.
39032              * @param {ColumnModel} this
39033              * @param {Number} columnIndex The column index
39034              * @param {Number} newWidth The new width
39035              */
39036             "widthchange": true,
39037         /**
39038              * @event headerchange
39039              * Fires when the text of a header changes.
39040              * @param {ColumnModel} this
39041              * @param {Number} columnIndex The column index
39042              * @param {Number} newText The new header text
39043              */
39044             "headerchange": true,
39045         /**
39046              * @event hiddenchange
39047              * Fires when a column is hidden or "unhidden".
39048              * @param {ColumnModel} this
39049              * @param {Number} columnIndex The column index
39050              * @param {Boolean} hidden true if hidden, false otherwise
39051              */
39052             "hiddenchange": true,
39053             /**
39054          * @event columnmoved
39055          * Fires when a column is moved.
39056          * @param {ColumnModel} this
39057          * @param {Number} oldIndex
39058          * @param {Number} newIndex
39059          */
39060         "columnmoved" : true,
39061         /**
39062          * @event columlockchange
39063          * Fires when a column's locked state is changed
39064          * @param {ColumnModel} this
39065          * @param {Number} colIndex
39066          * @param {Boolean} locked true if locked
39067          */
39068         "columnlockchange" : true
39069     });
39070     Roo.grid.ColumnModel.superclass.constructor.call(this);
39071 };
39072 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
39073     /**
39074      * @cfg {String} header The header text to display in the Grid view.
39075      */
39076     /**
39077      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
39078      * {@link Roo.data.Record} definition from which to draw the column's value. If not
39079      * specified, the column's index is used as an index into the Record's data Array.
39080      */
39081     /**
39082      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
39083      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
39084      */
39085     /**
39086      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
39087      * Defaults to the value of the {@link #defaultSortable} property.
39088      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
39089      */
39090     /**
39091      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
39092      */
39093     /**
39094      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
39095      */
39096     /**
39097      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
39098      */
39099     /**
39100      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
39101      */
39102     /**
39103      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
39104      * given the cell's data value. See {@link #setRenderer}. If not specified, the
39105      * default renderer uses the raw data value. If an object is returned (bootstrap only)
39106      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
39107      */
39108        /**
39109      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
39110      */
39111     /**
39112      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
39113      */
39114     /**
39115      * @cfg {String} cursor (Optional)
39116      */
39117     /**
39118      * @cfg {String} tooltip (Optional)
39119      */
39120     /**
39121      * @cfg {Number} xs (Optional)
39122      */
39123     /**
39124      * @cfg {Number} sm (Optional)
39125      */
39126     /**
39127      * @cfg {Number} md (Optional)
39128      */
39129     /**
39130      * @cfg {Number} lg (Optional)
39131      */
39132     /**
39133      * Returns the id of the column at the specified index.
39134      * @param {Number} index The column index
39135      * @return {String} the id
39136      */
39137     getColumnId : function(index){
39138         return this.config[index].id;
39139     },
39140
39141     /**
39142      * Returns the column for a specified id.
39143      * @param {String} id The column id
39144      * @return {Object} the column
39145      */
39146     getColumnById : function(id){
39147         return this.lookup[id];
39148     },
39149
39150     
39151     /**
39152      * Returns the column for a specified dataIndex.
39153      * @param {String} dataIndex The column dataIndex
39154      * @return {Object|Boolean} the column or false if not found
39155      */
39156     getColumnByDataIndex: function(dataIndex){
39157         var index = this.findColumnIndex(dataIndex);
39158         return index > -1 ? this.config[index] : false;
39159     },
39160     
39161     /**
39162      * Returns the index for a specified column id.
39163      * @param {String} id The column id
39164      * @return {Number} the index, or -1 if not found
39165      */
39166     getIndexById : function(id){
39167         for(var i = 0, len = this.config.length; i < len; i++){
39168             if(this.config[i].id == id){
39169                 return i;
39170             }
39171         }
39172         return -1;
39173     },
39174     
39175     /**
39176      * Returns the index for a specified column dataIndex.
39177      * @param {String} dataIndex The column dataIndex
39178      * @return {Number} the index, or -1 if not found
39179      */
39180     
39181     findColumnIndex : function(dataIndex){
39182         for(var i = 0, len = this.config.length; i < len; i++){
39183             if(this.config[i].dataIndex == dataIndex){
39184                 return i;
39185             }
39186         }
39187         return -1;
39188     },
39189     
39190     
39191     moveColumn : function(oldIndex, newIndex){
39192         var c = this.config[oldIndex];
39193         this.config.splice(oldIndex, 1);
39194         this.config.splice(newIndex, 0, c);
39195         this.dataMap = null;
39196         this.fireEvent("columnmoved", this, oldIndex, newIndex);
39197     },
39198
39199     isLocked : function(colIndex){
39200         return this.config[colIndex].locked === true;
39201     },
39202
39203     setLocked : function(colIndex, value, suppressEvent){
39204         if(this.isLocked(colIndex) == value){
39205             return;
39206         }
39207         this.config[colIndex].locked = value;
39208         if(!suppressEvent){
39209             this.fireEvent("columnlockchange", this, colIndex, value);
39210         }
39211     },
39212
39213     getTotalLockedWidth : function(){
39214         var totalWidth = 0;
39215         for(var i = 0; i < this.config.length; i++){
39216             if(this.isLocked(i) && !this.isHidden(i)){
39217                 this.totalWidth += this.getColumnWidth(i);
39218             }
39219         }
39220         return totalWidth;
39221     },
39222
39223     getLockedCount : function(){
39224         for(var i = 0, len = this.config.length; i < len; i++){
39225             if(!this.isLocked(i)){
39226                 return i;
39227             }
39228         }
39229         
39230         return this.config.length;
39231     },
39232
39233     /**
39234      * Returns the number of columns.
39235      * @return {Number}
39236      */
39237     getColumnCount : function(visibleOnly){
39238         if(visibleOnly === true){
39239             var c = 0;
39240             for(var i = 0, len = this.config.length; i < len; i++){
39241                 if(!this.isHidden(i)){
39242                     c++;
39243                 }
39244             }
39245             return c;
39246         }
39247         return this.config.length;
39248     },
39249
39250     /**
39251      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
39252      * @param {Function} fn
39253      * @param {Object} scope (optional)
39254      * @return {Array} result
39255      */
39256     getColumnsBy : function(fn, scope){
39257         var r = [];
39258         for(var i = 0, len = this.config.length; i < len; i++){
39259             var c = this.config[i];
39260             if(fn.call(scope||this, c, i) === true){
39261                 r[r.length] = c;
39262             }
39263         }
39264         return r;
39265     },
39266
39267     /**
39268      * Returns true if the specified column is sortable.
39269      * @param {Number} col The column index
39270      * @return {Boolean}
39271      */
39272     isSortable : function(col){
39273         if(typeof this.config[col].sortable == "undefined"){
39274             return this.defaultSortable;
39275         }
39276         return this.config[col].sortable;
39277     },
39278
39279     /**
39280      * Returns the rendering (formatting) function defined for the column.
39281      * @param {Number} col The column index.
39282      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
39283      */
39284     getRenderer : function(col){
39285         if(!this.config[col].renderer){
39286             return Roo.grid.ColumnModel.defaultRenderer;
39287         }
39288         return this.config[col].renderer;
39289     },
39290
39291     /**
39292      * Sets the rendering (formatting) function for a column.
39293      * @param {Number} col The column index
39294      * @param {Function} fn The function to use to process the cell's raw data
39295      * to return HTML markup for the grid view. The render function is called with
39296      * the following parameters:<ul>
39297      * <li>Data value.</li>
39298      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
39299      * <li>css A CSS style string to apply to the table cell.</li>
39300      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
39301      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
39302      * <li>Row index</li>
39303      * <li>Column index</li>
39304      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
39305      */
39306     setRenderer : function(col, fn){
39307         this.config[col].renderer = fn;
39308     },
39309
39310     /**
39311      * Returns the width for the specified column.
39312      * @param {Number} col The column index
39313      * @return {Number}
39314      */
39315     getColumnWidth : function(col){
39316         return this.config[col].width * 1 || this.defaultWidth;
39317     },
39318
39319     /**
39320      * Sets the width for a column.
39321      * @param {Number} col The column index
39322      * @param {Number} width The new width
39323      */
39324     setColumnWidth : function(col, width, suppressEvent){
39325         this.config[col].width = width;
39326         this.totalWidth = null;
39327         if(!suppressEvent){
39328              this.fireEvent("widthchange", this, col, width);
39329         }
39330     },
39331
39332     /**
39333      * Returns the total width of all columns.
39334      * @param {Boolean} includeHidden True to include hidden column widths
39335      * @return {Number}
39336      */
39337     getTotalWidth : function(includeHidden){
39338         if(!this.totalWidth){
39339             this.totalWidth = 0;
39340             for(var i = 0, len = this.config.length; i < len; i++){
39341                 if(includeHidden || !this.isHidden(i)){
39342                     this.totalWidth += this.getColumnWidth(i);
39343                 }
39344             }
39345         }
39346         return this.totalWidth;
39347     },
39348
39349     /**
39350      * Returns the header for the specified column.
39351      * @param {Number} col The column index
39352      * @return {String}
39353      */
39354     getColumnHeader : function(col){
39355         return this.config[col].header;
39356     },
39357
39358     /**
39359      * Sets the header for a column.
39360      * @param {Number} col The column index
39361      * @param {String} header The new header
39362      */
39363     setColumnHeader : function(col, header){
39364         this.config[col].header = header;
39365         this.fireEvent("headerchange", this, col, header);
39366     },
39367
39368     /**
39369      * Returns the tooltip for the specified column.
39370      * @param {Number} col The column index
39371      * @return {String}
39372      */
39373     getColumnTooltip : function(col){
39374             return this.config[col].tooltip;
39375     },
39376     /**
39377      * Sets the tooltip for a column.
39378      * @param {Number} col The column index
39379      * @param {String} tooltip The new tooltip
39380      */
39381     setColumnTooltip : function(col, tooltip){
39382             this.config[col].tooltip = tooltip;
39383     },
39384
39385     /**
39386      * Returns the dataIndex for the specified column.
39387      * @param {Number} col The column index
39388      * @return {Number}
39389      */
39390     getDataIndex : function(col){
39391         return this.config[col].dataIndex;
39392     },
39393
39394     /**
39395      * Sets the dataIndex for a column.
39396      * @param {Number} col The column index
39397      * @param {Number} dataIndex The new dataIndex
39398      */
39399     setDataIndex : function(col, dataIndex){
39400         this.config[col].dataIndex = dataIndex;
39401     },
39402
39403     
39404     
39405     /**
39406      * Returns true if the cell is editable.
39407      * @param {Number} colIndex The column index
39408      * @param {Number} rowIndex The row index - this is nto actually used..?
39409      * @return {Boolean}
39410      */
39411     isCellEditable : function(colIndex, rowIndex){
39412         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
39413     },
39414
39415     /**
39416      * Returns the editor defined for the cell/column.
39417      * return false or null to disable editing.
39418      * @param {Number} colIndex The column index
39419      * @param {Number} rowIndex The row index
39420      * @return {Object}
39421      */
39422     getCellEditor : function(colIndex, rowIndex){
39423         return this.config[colIndex].editor;
39424     },
39425
39426     /**
39427      * Sets if a column is editable.
39428      * @param {Number} col The column index
39429      * @param {Boolean} editable True if the column is editable
39430      */
39431     setEditable : function(col, editable){
39432         this.config[col].editable = editable;
39433     },
39434
39435
39436     /**
39437      * Returns true if the column is hidden.
39438      * @param {Number} colIndex The column index
39439      * @return {Boolean}
39440      */
39441     isHidden : function(colIndex){
39442         return this.config[colIndex].hidden;
39443     },
39444
39445
39446     /**
39447      * Returns true if the column width cannot be changed
39448      */
39449     isFixed : function(colIndex){
39450         return this.config[colIndex].fixed;
39451     },
39452
39453     /**
39454      * Returns true if the column can be resized
39455      * @return {Boolean}
39456      */
39457     isResizable : function(colIndex){
39458         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
39459     },
39460     /**
39461      * Sets if a column is hidden.
39462      * @param {Number} colIndex The column index
39463      * @param {Boolean} hidden True if the column is hidden
39464      */
39465     setHidden : function(colIndex, hidden){
39466         this.config[colIndex].hidden = hidden;
39467         this.totalWidth = null;
39468         this.fireEvent("hiddenchange", this, colIndex, hidden);
39469     },
39470
39471     /**
39472      * Sets the editor for a column.
39473      * @param {Number} col The column index
39474      * @param {Object} editor The editor object
39475      */
39476     setEditor : function(col, editor){
39477         this.config[col].editor = editor;
39478     }
39479 });
39480
39481 Roo.grid.ColumnModel.defaultRenderer = function(value){
39482         if(typeof value == "string" && value.length < 1){
39483             return "&#160;";
39484         }
39485         return value;
39486 };
39487
39488 // Alias for backwards compatibility
39489 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
39490 /*
39491  * Based on:
39492  * Ext JS Library 1.1.1
39493  * Copyright(c) 2006-2007, Ext JS, LLC.
39494  *
39495  * Originally Released Under LGPL - original licence link has changed is not relivant.
39496  *
39497  * Fork - LGPL
39498  * <script type="text/javascript">
39499  */
39500
39501 /**
39502  * @class Roo.grid.AbstractSelectionModel
39503  * @extends Roo.util.Observable
39504  * Abstract base class for grid SelectionModels.  It provides the interface that should be
39505  * implemented by descendant classes.  This class should not be directly instantiated.
39506  * @constructor
39507  */
39508 Roo.grid.AbstractSelectionModel = function(){
39509     this.locked = false;
39510     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
39511 };
39512
39513 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
39514     /** @ignore Called by the grid automatically. Do not call directly. */
39515     init : function(grid){
39516         this.grid = grid;
39517         this.initEvents();
39518     },
39519
39520     /**
39521      * Locks the selections.
39522      */
39523     lock : function(){
39524         this.locked = true;
39525     },
39526
39527     /**
39528      * Unlocks the selections.
39529      */
39530     unlock : function(){
39531         this.locked = false;
39532     },
39533
39534     /**
39535      * Returns true if the selections are locked.
39536      * @return {Boolean}
39537      */
39538     isLocked : function(){
39539         return this.locked;
39540     }
39541 });/*
39542  * Based on:
39543  * Ext JS Library 1.1.1
39544  * Copyright(c) 2006-2007, Ext JS, LLC.
39545  *
39546  * Originally Released Under LGPL - original licence link has changed is not relivant.
39547  *
39548  * Fork - LGPL
39549  * <script type="text/javascript">
39550  */
39551 /**
39552  * @extends Roo.grid.AbstractSelectionModel
39553  * @class Roo.grid.RowSelectionModel
39554  * The default SelectionModel used by {@link Roo.grid.Grid}.
39555  * It supports multiple selections and keyboard selection/navigation. 
39556  * @constructor
39557  * @param {Object} config
39558  */
39559 Roo.grid.RowSelectionModel = function(config){
39560     Roo.apply(this, config);
39561     this.selections = new Roo.util.MixedCollection(false, function(o){
39562         return o.id;
39563     });
39564
39565     this.last = false;
39566     this.lastActive = false;
39567
39568     this.addEvents({
39569         /**
39570              * @event selectionchange
39571              * Fires when the selection changes
39572              * @param {SelectionModel} this
39573              */
39574             "selectionchange" : true,
39575         /**
39576              * @event afterselectionchange
39577              * Fires after the selection changes (eg. by key press or clicking)
39578              * @param {SelectionModel} this
39579              */
39580             "afterselectionchange" : true,
39581         /**
39582              * @event beforerowselect
39583              * Fires when a row is selected being selected, return false to cancel.
39584              * @param {SelectionModel} this
39585              * @param {Number} rowIndex The selected index
39586              * @param {Boolean} keepExisting False if other selections will be cleared
39587              */
39588             "beforerowselect" : true,
39589         /**
39590              * @event rowselect
39591              * Fires when a row is selected.
39592              * @param {SelectionModel} this
39593              * @param {Number} rowIndex The selected index
39594              * @param {Roo.data.Record} r The record
39595              */
39596             "rowselect" : true,
39597         /**
39598              * @event rowdeselect
39599              * Fires when a row is deselected.
39600              * @param {SelectionModel} this
39601              * @param {Number} rowIndex The selected index
39602              */
39603         "rowdeselect" : true
39604     });
39605     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
39606     this.locked = false;
39607 };
39608
39609 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
39610     /**
39611      * @cfg {Boolean} singleSelect
39612      * True to allow selection of only one row at a time (defaults to false)
39613      */
39614     singleSelect : false,
39615
39616     // private
39617     initEvents : function(){
39618
39619         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
39620             this.grid.on("mousedown", this.handleMouseDown, this);
39621         }else{ // allow click to work like normal
39622             this.grid.on("rowclick", this.handleDragableRowClick, this);
39623         }
39624
39625         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
39626             "up" : function(e){
39627                 if(!e.shiftKey){
39628                     this.selectPrevious(e.shiftKey);
39629                 }else if(this.last !== false && this.lastActive !== false){
39630                     var last = this.last;
39631                     this.selectRange(this.last,  this.lastActive-1);
39632                     this.grid.getView().focusRow(this.lastActive);
39633                     if(last !== false){
39634                         this.last = last;
39635                     }
39636                 }else{
39637                     this.selectFirstRow();
39638                 }
39639                 this.fireEvent("afterselectionchange", this);
39640             },
39641             "down" : function(e){
39642                 if(!e.shiftKey){
39643                     this.selectNext(e.shiftKey);
39644                 }else if(this.last !== false && this.lastActive !== false){
39645                     var last = this.last;
39646                     this.selectRange(this.last,  this.lastActive+1);
39647                     this.grid.getView().focusRow(this.lastActive);
39648                     if(last !== false){
39649                         this.last = last;
39650                     }
39651                 }else{
39652                     this.selectFirstRow();
39653                 }
39654                 this.fireEvent("afterselectionchange", this);
39655             },
39656             scope: this
39657         });
39658
39659         var view = this.grid.view;
39660         view.on("refresh", this.onRefresh, this);
39661         view.on("rowupdated", this.onRowUpdated, this);
39662         view.on("rowremoved", this.onRemove, this);
39663     },
39664
39665     // private
39666     onRefresh : function(){
39667         var ds = this.grid.dataSource, i, v = this.grid.view;
39668         var s = this.selections;
39669         s.each(function(r){
39670             if((i = ds.indexOfId(r.id)) != -1){
39671                 v.onRowSelect(i);
39672                 s.add(ds.getAt(i)); // updating the selection relate data
39673             }else{
39674                 s.remove(r);
39675             }
39676         });
39677     },
39678
39679     // private
39680     onRemove : function(v, index, r){
39681         this.selections.remove(r);
39682     },
39683
39684     // private
39685     onRowUpdated : function(v, index, r){
39686         if(this.isSelected(r)){
39687             v.onRowSelect(index);
39688         }
39689     },
39690
39691     /**
39692      * Select records.
39693      * @param {Array} records The records to select
39694      * @param {Boolean} keepExisting (optional) True to keep existing selections
39695      */
39696     selectRecords : function(records, keepExisting){
39697         if(!keepExisting){
39698             this.clearSelections();
39699         }
39700         var ds = this.grid.dataSource;
39701         for(var i = 0, len = records.length; i < len; i++){
39702             this.selectRow(ds.indexOf(records[i]), true);
39703         }
39704     },
39705
39706     /**
39707      * Gets the number of selected rows.
39708      * @return {Number}
39709      */
39710     getCount : function(){
39711         return this.selections.length;
39712     },
39713
39714     /**
39715      * Selects the first row in the grid.
39716      */
39717     selectFirstRow : function(){
39718         this.selectRow(0);
39719     },
39720
39721     /**
39722      * Select the last row.
39723      * @param {Boolean} keepExisting (optional) True to keep existing selections
39724      */
39725     selectLastRow : function(keepExisting){
39726         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
39727     },
39728
39729     /**
39730      * Selects the row immediately following the last selected row.
39731      * @param {Boolean} keepExisting (optional) True to keep existing selections
39732      */
39733     selectNext : function(keepExisting){
39734         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
39735             this.selectRow(this.last+1, keepExisting);
39736             this.grid.getView().focusRow(this.last);
39737         }
39738     },
39739
39740     /**
39741      * Selects the row that precedes the last selected row.
39742      * @param {Boolean} keepExisting (optional) True to keep existing selections
39743      */
39744     selectPrevious : function(keepExisting){
39745         if(this.last){
39746             this.selectRow(this.last-1, keepExisting);
39747             this.grid.getView().focusRow(this.last);
39748         }
39749     },
39750
39751     /**
39752      * Returns the selected records
39753      * @return {Array} Array of selected records
39754      */
39755     getSelections : function(){
39756         return [].concat(this.selections.items);
39757     },
39758
39759     /**
39760      * Returns the first selected record.
39761      * @return {Record}
39762      */
39763     getSelected : function(){
39764         return this.selections.itemAt(0);
39765     },
39766
39767
39768     /**
39769      * Clears all selections.
39770      */
39771     clearSelections : function(fast){
39772         if(this.locked) {
39773             return;
39774         }
39775         if(fast !== true){
39776             var ds = this.grid.dataSource;
39777             var s = this.selections;
39778             s.each(function(r){
39779                 this.deselectRow(ds.indexOfId(r.id));
39780             }, this);
39781             s.clear();
39782         }else{
39783             this.selections.clear();
39784         }
39785         this.last = false;
39786     },
39787
39788
39789     /**
39790      * Selects all rows.
39791      */
39792     selectAll : function(){
39793         if(this.locked) {
39794             return;
39795         }
39796         this.selections.clear();
39797         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
39798             this.selectRow(i, true);
39799         }
39800     },
39801
39802     /**
39803      * Returns True if there is a selection.
39804      * @return {Boolean}
39805      */
39806     hasSelection : function(){
39807         return this.selections.length > 0;
39808     },
39809
39810     /**
39811      * Returns True if the specified row is selected.
39812      * @param {Number/Record} record The record or index of the record to check
39813      * @return {Boolean}
39814      */
39815     isSelected : function(index){
39816         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
39817         return (r && this.selections.key(r.id) ? true : false);
39818     },
39819
39820     /**
39821      * Returns True if the specified record id is selected.
39822      * @param {String} id The id of record to check
39823      * @return {Boolean}
39824      */
39825     isIdSelected : function(id){
39826         return (this.selections.key(id) ? true : false);
39827     },
39828
39829     // private
39830     handleMouseDown : function(e, t){
39831         var view = this.grid.getView(), rowIndex;
39832         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
39833             return;
39834         };
39835         if(e.shiftKey && this.last !== false){
39836             var last = this.last;
39837             this.selectRange(last, rowIndex, e.ctrlKey);
39838             this.last = last; // reset the last
39839             view.focusRow(rowIndex);
39840         }else{
39841             var isSelected = this.isSelected(rowIndex);
39842             if(e.button !== 0 && isSelected){
39843                 view.focusRow(rowIndex);
39844             }else if(e.ctrlKey && isSelected){
39845                 this.deselectRow(rowIndex);
39846             }else if(!isSelected){
39847                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
39848                 view.focusRow(rowIndex);
39849             }
39850         }
39851         this.fireEvent("afterselectionchange", this);
39852     },
39853     // private
39854     handleDragableRowClick :  function(grid, rowIndex, e) 
39855     {
39856         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
39857             this.selectRow(rowIndex, false);
39858             grid.view.focusRow(rowIndex);
39859              this.fireEvent("afterselectionchange", this);
39860         }
39861     },
39862     
39863     /**
39864      * Selects multiple rows.
39865      * @param {Array} rows Array of the indexes of the row to select
39866      * @param {Boolean} keepExisting (optional) True to keep existing selections
39867      */
39868     selectRows : function(rows, keepExisting){
39869         if(!keepExisting){
39870             this.clearSelections();
39871         }
39872         for(var i = 0, len = rows.length; i < len; i++){
39873             this.selectRow(rows[i], true);
39874         }
39875     },
39876
39877     /**
39878      * Selects a range of rows. All rows in between startRow and endRow are also selected.
39879      * @param {Number} startRow The index of the first row in the range
39880      * @param {Number} endRow The index of the last row in the range
39881      * @param {Boolean} keepExisting (optional) True to retain existing selections
39882      */
39883     selectRange : function(startRow, endRow, keepExisting){
39884         if(this.locked) {
39885             return;
39886         }
39887         if(!keepExisting){
39888             this.clearSelections();
39889         }
39890         if(startRow <= endRow){
39891             for(var i = startRow; i <= endRow; i++){
39892                 this.selectRow(i, true);
39893             }
39894         }else{
39895             for(var i = startRow; i >= endRow; i--){
39896                 this.selectRow(i, true);
39897             }
39898         }
39899     },
39900
39901     /**
39902      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
39903      * @param {Number} startRow The index of the first row in the range
39904      * @param {Number} endRow The index of the last row in the range
39905      */
39906     deselectRange : function(startRow, endRow, preventViewNotify){
39907         if(this.locked) {
39908             return;
39909         }
39910         for(var i = startRow; i <= endRow; i++){
39911             this.deselectRow(i, preventViewNotify);
39912         }
39913     },
39914
39915     /**
39916      * Selects a row.
39917      * @param {Number} row The index of the row to select
39918      * @param {Boolean} keepExisting (optional) True to keep existing selections
39919      */
39920     selectRow : function(index, keepExisting, preventViewNotify){
39921         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
39922             return;
39923         }
39924         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
39925             if(!keepExisting || this.singleSelect){
39926                 this.clearSelections();
39927             }
39928             var r = this.grid.dataSource.getAt(index);
39929             this.selections.add(r);
39930             this.last = this.lastActive = index;
39931             if(!preventViewNotify){
39932                 this.grid.getView().onRowSelect(index);
39933             }
39934             this.fireEvent("rowselect", this, index, r);
39935             this.fireEvent("selectionchange", this);
39936         }
39937     },
39938
39939     /**
39940      * Deselects a row.
39941      * @param {Number} row The index of the row to deselect
39942      */
39943     deselectRow : function(index, preventViewNotify){
39944         if(this.locked) {
39945             return;
39946         }
39947         if(this.last == index){
39948             this.last = false;
39949         }
39950         if(this.lastActive == index){
39951             this.lastActive = false;
39952         }
39953         var r = this.grid.dataSource.getAt(index);
39954         this.selections.remove(r);
39955         if(!preventViewNotify){
39956             this.grid.getView().onRowDeselect(index);
39957         }
39958         this.fireEvent("rowdeselect", this, index);
39959         this.fireEvent("selectionchange", this);
39960     },
39961
39962     // private
39963     restoreLast : function(){
39964         if(this._last){
39965             this.last = this._last;
39966         }
39967     },
39968
39969     // private
39970     acceptsNav : function(row, col, cm){
39971         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39972     },
39973
39974     // private
39975     onEditorKey : function(field, e){
39976         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
39977         if(k == e.TAB){
39978             e.stopEvent();
39979             ed.completeEdit();
39980             if(e.shiftKey){
39981                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39982             }else{
39983                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39984             }
39985         }else if(k == e.ENTER && !e.ctrlKey){
39986             e.stopEvent();
39987             ed.completeEdit();
39988             if(e.shiftKey){
39989                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
39990             }else{
39991                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
39992             }
39993         }else if(k == e.ESC){
39994             ed.cancelEdit();
39995         }
39996         if(newCell){
39997             g.startEditing(newCell[0], newCell[1]);
39998         }
39999     }
40000 });/*
40001  * Based on:
40002  * Ext JS Library 1.1.1
40003  * Copyright(c) 2006-2007, Ext JS, LLC.
40004  *
40005  * Originally Released Under LGPL - original licence link has changed is not relivant.
40006  *
40007  * Fork - LGPL
40008  * <script type="text/javascript">
40009  */
40010 /**
40011  * @class Roo.grid.CellSelectionModel
40012  * @extends Roo.grid.AbstractSelectionModel
40013  * This class provides the basic implementation for cell selection in a grid.
40014  * @constructor
40015  * @param {Object} config The object containing the configuration of this model.
40016  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
40017  */
40018 Roo.grid.CellSelectionModel = function(config){
40019     Roo.apply(this, config);
40020
40021     this.selection = null;
40022
40023     this.addEvents({
40024         /**
40025              * @event beforerowselect
40026              * Fires before a cell is selected.
40027              * @param {SelectionModel} this
40028              * @param {Number} rowIndex The selected row index
40029              * @param {Number} colIndex The selected cell index
40030              */
40031             "beforecellselect" : true,
40032         /**
40033              * @event cellselect
40034              * Fires when a cell is selected.
40035              * @param {SelectionModel} this
40036              * @param {Number} rowIndex The selected row index
40037              * @param {Number} colIndex The selected cell index
40038              */
40039             "cellselect" : true,
40040         /**
40041              * @event selectionchange
40042              * Fires when the active selection changes.
40043              * @param {SelectionModel} this
40044              * @param {Object} selection null for no selection or an object (o) with two properties
40045                 <ul>
40046                 <li>o.record: the record object for the row the selection is in</li>
40047                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
40048                 </ul>
40049              */
40050             "selectionchange" : true,
40051         /**
40052              * @event tabend
40053              * Fires when the tab (or enter) was pressed on the last editable cell
40054              * You can use this to trigger add new row.
40055              * @param {SelectionModel} this
40056              */
40057             "tabend" : true,
40058          /**
40059              * @event beforeeditnext
40060              * Fires before the next editable sell is made active
40061              * You can use this to skip to another cell or fire the tabend
40062              *    if you set cell to false
40063              * @param {Object} eventdata object : { cell : [ row, col ] } 
40064              */
40065             "beforeeditnext" : true
40066     });
40067     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
40068 };
40069
40070 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
40071     
40072     enter_is_tab: false,
40073
40074     /** @ignore */
40075     initEvents : function(){
40076         this.grid.on("mousedown", this.handleMouseDown, this);
40077         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
40078         var view = this.grid.view;
40079         view.on("refresh", this.onViewChange, this);
40080         view.on("rowupdated", this.onRowUpdated, this);
40081         view.on("beforerowremoved", this.clearSelections, this);
40082         view.on("beforerowsinserted", this.clearSelections, this);
40083         if(this.grid.isEditor){
40084             this.grid.on("beforeedit", this.beforeEdit,  this);
40085         }
40086     },
40087
40088         //private
40089     beforeEdit : function(e){
40090         this.select(e.row, e.column, false, true, e.record);
40091     },
40092
40093         //private
40094     onRowUpdated : function(v, index, r){
40095         if(this.selection && this.selection.record == r){
40096             v.onCellSelect(index, this.selection.cell[1]);
40097         }
40098     },
40099
40100         //private
40101     onViewChange : function(){
40102         this.clearSelections(true);
40103     },
40104
40105         /**
40106          * Returns the currently selected cell,.
40107          * @return {Array} The selected cell (row, column) or null if none selected.
40108          */
40109     getSelectedCell : function(){
40110         return this.selection ? this.selection.cell : null;
40111     },
40112
40113     /**
40114      * Clears all selections.
40115      * @param {Boolean} true to prevent the gridview from being notified about the change.
40116      */
40117     clearSelections : function(preventNotify){
40118         var s = this.selection;
40119         if(s){
40120             if(preventNotify !== true){
40121                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
40122             }
40123             this.selection = null;
40124             this.fireEvent("selectionchange", this, null);
40125         }
40126     },
40127
40128     /**
40129      * Returns true if there is a selection.
40130      * @return {Boolean}
40131      */
40132     hasSelection : function(){
40133         return this.selection ? true : false;
40134     },
40135
40136     /** @ignore */
40137     handleMouseDown : function(e, t){
40138         var v = this.grid.getView();
40139         if(this.isLocked()){
40140             return;
40141         };
40142         var row = v.findRowIndex(t);
40143         var cell = v.findCellIndex(t);
40144         if(row !== false && cell !== false){
40145             this.select(row, cell);
40146         }
40147     },
40148
40149     /**
40150      * Selects a cell.
40151      * @param {Number} rowIndex
40152      * @param {Number} collIndex
40153      */
40154     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
40155         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
40156             this.clearSelections();
40157             r = r || this.grid.dataSource.getAt(rowIndex);
40158             this.selection = {
40159                 record : r,
40160                 cell : [rowIndex, colIndex]
40161             };
40162             if(!preventViewNotify){
40163                 var v = this.grid.getView();
40164                 v.onCellSelect(rowIndex, colIndex);
40165                 if(preventFocus !== true){
40166                     v.focusCell(rowIndex, colIndex);
40167                 }
40168             }
40169             this.fireEvent("cellselect", this, rowIndex, colIndex);
40170             this.fireEvent("selectionchange", this, this.selection);
40171         }
40172     },
40173
40174         //private
40175     isSelectable : function(rowIndex, colIndex, cm){
40176         return !cm.isHidden(colIndex);
40177     },
40178
40179     /** @ignore */
40180     handleKeyDown : function(e){
40181         //Roo.log('Cell Sel Model handleKeyDown');
40182         if(!e.isNavKeyPress()){
40183             return;
40184         }
40185         var g = this.grid, s = this.selection;
40186         if(!s){
40187             e.stopEvent();
40188             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
40189             if(cell){
40190                 this.select(cell[0], cell[1]);
40191             }
40192             return;
40193         }
40194         var sm = this;
40195         var walk = function(row, col, step){
40196             return g.walkCells(row, col, step, sm.isSelectable,  sm);
40197         };
40198         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
40199         var newCell;
40200
40201       
40202
40203         switch(k){
40204             case e.TAB:
40205                 // handled by onEditorKey
40206                 if (g.isEditor && g.editing) {
40207                     return;
40208                 }
40209                 if(e.shiftKey) {
40210                     newCell = walk(r, c-1, -1);
40211                 } else {
40212                     newCell = walk(r, c+1, 1);
40213                 }
40214                 break;
40215             
40216             case e.DOWN:
40217                newCell = walk(r+1, c, 1);
40218                 break;
40219             
40220             case e.UP:
40221                 newCell = walk(r-1, c, -1);
40222                 break;
40223             
40224             case e.RIGHT:
40225                 newCell = walk(r, c+1, 1);
40226                 break;
40227             
40228             case e.LEFT:
40229                 newCell = walk(r, c-1, -1);
40230                 break;
40231             
40232             case e.ENTER:
40233                 
40234                 if(g.isEditor && !g.editing){
40235                    g.startEditing(r, c);
40236                    e.stopEvent();
40237                    return;
40238                 }
40239                 
40240                 
40241              break;
40242         };
40243         if(newCell){
40244             this.select(newCell[0], newCell[1]);
40245             e.stopEvent();
40246             
40247         }
40248     },
40249
40250     acceptsNav : function(row, col, cm){
40251         return !cm.isHidden(col) && cm.isCellEditable(col, row);
40252     },
40253     /**
40254      * Selects a cell.
40255      * @param {Number} field (not used) - as it's normally used as a listener
40256      * @param {Number} e - event - fake it by using
40257      *
40258      * var e = Roo.EventObjectImpl.prototype;
40259      * e.keyCode = e.TAB
40260      *
40261      * 
40262      */
40263     onEditorKey : function(field, e){
40264         
40265         var k = e.getKey(),
40266             newCell,
40267             g = this.grid,
40268             ed = g.activeEditor,
40269             forward = false;
40270         ///Roo.log('onEditorKey' + k);
40271         
40272         
40273         if (this.enter_is_tab && k == e.ENTER) {
40274             k = e.TAB;
40275         }
40276         
40277         if(k == e.TAB){
40278             if(e.shiftKey){
40279                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
40280             }else{
40281                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
40282                 forward = true;
40283             }
40284             
40285             e.stopEvent();
40286             
40287         } else if(k == e.ENTER &&  !e.ctrlKey){
40288             ed.completeEdit();
40289             e.stopEvent();
40290             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
40291         
40292                 } else if(k == e.ESC){
40293             ed.cancelEdit();
40294         }
40295                 
40296         if (newCell) {
40297             var ecall = { cell : newCell, forward : forward };
40298             this.fireEvent('beforeeditnext', ecall );
40299             newCell = ecall.cell;
40300                         forward = ecall.forward;
40301         }
40302                 
40303         if(newCell){
40304             //Roo.log('next cell after edit');
40305             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
40306         } else if (forward) {
40307             // tabbed past last
40308             this.fireEvent.defer(100, this, ['tabend',this]);
40309         }
40310     }
40311 });/*
40312  * Based on:
40313  * Ext JS Library 1.1.1
40314  * Copyright(c) 2006-2007, Ext JS, LLC.
40315  *
40316  * Originally Released Under LGPL - original licence link has changed is not relivant.
40317  *
40318  * Fork - LGPL
40319  * <script type="text/javascript">
40320  */
40321  
40322 /**
40323  * @class Roo.grid.EditorGrid
40324  * @extends Roo.grid.Grid
40325  * Class for creating and editable grid.
40326  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
40327  * The container MUST have some type of size defined for the grid to fill. The container will be 
40328  * automatically set to position relative if it isn't already.
40329  * @param {Object} dataSource The data model to bind to
40330  * @param {Object} colModel The column model with info about this grid's columns
40331  */
40332 Roo.grid.EditorGrid = function(container, config){
40333     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
40334     this.getGridEl().addClass("xedit-grid");
40335
40336     if(!this.selModel){
40337         this.selModel = new Roo.grid.CellSelectionModel();
40338     }
40339
40340     this.activeEditor = null;
40341
40342         this.addEvents({
40343             /**
40344              * @event beforeedit
40345              * Fires before cell editing is triggered. The edit event object has the following properties <br />
40346              * <ul style="padding:5px;padding-left:16px;">
40347              * <li>grid - This grid</li>
40348              * <li>record - The record being edited</li>
40349              * <li>field - The field name being edited</li>
40350              * <li>value - The value for the field being edited.</li>
40351              * <li>row - The grid row index</li>
40352              * <li>column - The grid column index</li>
40353              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
40354              * </ul>
40355              * @param {Object} e An edit event (see above for description)
40356              */
40357             "beforeedit" : true,
40358             /**
40359              * @event afteredit
40360              * Fires after a cell is edited. <br />
40361              * <ul style="padding:5px;padding-left:16px;">
40362              * <li>grid - This grid</li>
40363              * <li>record - The record being edited</li>
40364              * <li>field - The field name being edited</li>
40365              * <li>value - The value being set</li>
40366              * <li>originalValue - The original value for the field, before the edit.</li>
40367              * <li>row - The grid row index</li>
40368              * <li>column - The grid column index</li>
40369              * </ul>
40370              * @param {Object} e An edit event (see above for description)
40371              */
40372             "afteredit" : true,
40373             /**
40374              * @event validateedit
40375              * Fires after a cell is edited, but before the value is set in the record. 
40376          * You can use this to modify the value being set in the field, Return false
40377              * to cancel the change. The edit event object has the following properties <br />
40378              * <ul style="padding:5px;padding-left:16px;">
40379          * <li>editor - This editor</li>
40380              * <li>grid - This grid</li>
40381              * <li>record - The record being edited</li>
40382              * <li>field - The field name being edited</li>
40383              * <li>value - The value being set</li>
40384              * <li>originalValue - The original value for the field, before the edit.</li>
40385              * <li>row - The grid row index</li>
40386              * <li>column - The grid column index</li>
40387              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
40388              * </ul>
40389              * @param {Object} e An edit event (see above for description)
40390              */
40391             "validateedit" : true
40392         });
40393     this.on("bodyscroll", this.stopEditing,  this);
40394     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
40395 };
40396
40397 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
40398     /**
40399      * @cfg {Number} clicksToEdit
40400      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
40401      */
40402     clicksToEdit: 2,
40403
40404     // private
40405     isEditor : true,
40406     // private
40407     trackMouseOver: false, // causes very odd FF errors
40408
40409     onCellDblClick : function(g, row, col){
40410         this.startEditing(row, col);
40411     },
40412
40413     onEditComplete : function(ed, value, startValue){
40414         this.editing = false;
40415         this.activeEditor = null;
40416         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
40417         var r = ed.record;
40418         var field = this.colModel.getDataIndex(ed.col);
40419         var e = {
40420             grid: this,
40421             record: r,
40422             field: field,
40423             originalValue: startValue,
40424             value: value,
40425             row: ed.row,
40426             column: ed.col,
40427             cancel:false,
40428             editor: ed
40429         };
40430         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
40431         cell.show();
40432           
40433         if(String(value) !== String(startValue)){
40434             
40435             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
40436                 r.set(field, e.value);
40437                 // if we are dealing with a combo box..
40438                 // then we also set the 'name' colum to be the displayField
40439                 if (ed.field.displayField && ed.field.name) {
40440                     r.set(ed.field.name, ed.field.el.dom.value);
40441                 }
40442                 
40443                 delete e.cancel; //?? why!!!
40444                 this.fireEvent("afteredit", e);
40445             }
40446         } else {
40447             this.fireEvent("afteredit", e); // always fire it!
40448         }
40449         this.view.focusCell(ed.row, ed.col);
40450     },
40451
40452     /**
40453      * Starts editing the specified for the specified row/column
40454      * @param {Number} rowIndex
40455      * @param {Number} colIndex
40456      */
40457     startEditing : function(row, col){
40458         this.stopEditing();
40459         if(this.colModel.isCellEditable(col, row)){
40460             this.view.ensureVisible(row, col, true);
40461           
40462             var r = this.dataSource.getAt(row);
40463             var field = this.colModel.getDataIndex(col);
40464             var cell = Roo.get(this.view.getCell(row,col));
40465             var e = {
40466                 grid: this,
40467                 record: r,
40468                 field: field,
40469                 value: r.data[field],
40470                 row: row,
40471                 column: col,
40472                 cancel:false 
40473             };
40474             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
40475                 this.editing = true;
40476                 var ed = this.colModel.getCellEditor(col, row);
40477                 
40478                 if (!ed) {
40479                     return;
40480                 }
40481                 if(!ed.rendered){
40482                     ed.render(ed.parentEl || document.body);
40483                 }
40484                 ed.field.reset();
40485                
40486                 cell.hide();
40487                 
40488                 (function(){ // complex but required for focus issues in safari, ie and opera
40489                     ed.row = row;
40490                     ed.col = col;
40491                     ed.record = r;
40492                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
40493                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
40494                     this.activeEditor = ed;
40495                     var v = r.data[field];
40496                     ed.startEdit(this.view.getCell(row, col), v);
40497                     // combo's with 'displayField and name set
40498                     if (ed.field.displayField && ed.field.name) {
40499                         ed.field.el.dom.value = r.data[ed.field.name];
40500                     }
40501                     
40502                     
40503                 }).defer(50, this);
40504             }
40505         }
40506     },
40507         
40508     /**
40509      * Stops any active editing
40510      */
40511     stopEditing : function(){
40512         if(this.activeEditor){
40513             this.activeEditor.completeEdit();
40514         }
40515         this.activeEditor = null;
40516     },
40517         
40518          /**
40519      * Called to get grid's drag proxy text, by default returns this.ddText.
40520      * @return {String}
40521      */
40522     getDragDropText : function(){
40523         var count = this.selModel.getSelectedCell() ? 1 : 0;
40524         return String.format(this.ddText, count, count == 1 ? '' : 's');
40525     }
40526         
40527 });/*
40528  * Based on:
40529  * Ext JS Library 1.1.1
40530  * Copyright(c) 2006-2007, Ext JS, LLC.
40531  *
40532  * Originally Released Under LGPL - original licence link has changed is not relivant.
40533  *
40534  * Fork - LGPL
40535  * <script type="text/javascript">
40536  */
40537
40538 // private - not really -- you end up using it !
40539 // This is a support class used internally by the Grid components
40540
40541 /**
40542  * @class Roo.grid.GridEditor
40543  * @extends Roo.Editor
40544  * Class for creating and editable grid elements.
40545  * @param {Object} config any settings (must include field)
40546  */
40547 Roo.grid.GridEditor = function(field, config){
40548     if (!config && field.field) {
40549         config = field;
40550         field = Roo.factory(config.field, Roo.form);
40551     }
40552     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
40553     field.monitorTab = false;
40554 };
40555
40556 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
40557     
40558     /**
40559      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
40560      */
40561     
40562     alignment: "tl-tl",
40563     autoSize: "width",
40564     hideEl : false,
40565     cls: "x-small-editor x-grid-editor",
40566     shim:false,
40567     shadow:"frame"
40568 });/*
40569  * Based on:
40570  * Ext JS Library 1.1.1
40571  * Copyright(c) 2006-2007, Ext JS, LLC.
40572  *
40573  * Originally Released Under LGPL - original licence link has changed is not relivant.
40574  *
40575  * Fork - LGPL
40576  * <script type="text/javascript">
40577  */
40578   
40579
40580   
40581 Roo.grid.PropertyRecord = Roo.data.Record.create([
40582     {name:'name',type:'string'},  'value'
40583 ]);
40584
40585
40586 Roo.grid.PropertyStore = function(grid, source){
40587     this.grid = grid;
40588     this.store = new Roo.data.Store({
40589         recordType : Roo.grid.PropertyRecord
40590     });
40591     this.store.on('update', this.onUpdate,  this);
40592     if(source){
40593         this.setSource(source);
40594     }
40595     Roo.grid.PropertyStore.superclass.constructor.call(this);
40596 };
40597
40598
40599
40600 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
40601     setSource : function(o){
40602         this.source = o;
40603         this.store.removeAll();
40604         var data = [];
40605         for(var k in o){
40606             if(this.isEditableValue(o[k])){
40607                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
40608             }
40609         }
40610         this.store.loadRecords({records: data}, {}, true);
40611     },
40612
40613     onUpdate : function(ds, record, type){
40614         if(type == Roo.data.Record.EDIT){
40615             var v = record.data['value'];
40616             var oldValue = record.modified['value'];
40617             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
40618                 this.source[record.id] = v;
40619                 record.commit();
40620                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
40621             }else{
40622                 record.reject();
40623             }
40624         }
40625     },
40626
40627     getProperty : function(row){
40628        return this.store.getAt(row);
40629     },
40630
40631     isEditableValue: function(val){
40632         if(val && val instanceof Date){
40633             return true;
40634         }else if(typeof val == 'object' || typeof val == 'function'){
40635             return false;
40636         }
40637         return true;
40638     },
40639
40640     setValue : function(prop, value){
40641         this.source[prop] = value;
40642         this.store.getById(prop).set('value', value);
40643     },
40644
40645     getSource : function(){
40646         return this.source;
40647     }
40648 });
40649
40650 Roo.grid.PropertyColumnModel = function(grid, store){
40651     this.grid = grid;
40652     var g = Roo.grid;
40653     g.PropertyColumnModel.superclass.constructor.call(this, [
40654         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
40655         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
40656     ]);
40657     this.store = store;
40658     this.bselect = Roo.DomHelper.append(document.body, {
40659         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
40660             {tag: 'option', value: 'true', html: 'true'},
40661             {tag: 'option', value: 'false', html: 'false'}
40662         ]
40663     });
40664     Roo.id(this.bselect);
40665     var f = Roo.form;
40666     this.editors = {
40667         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
40668         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
40669         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
40670         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
40671         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
40672     };
40673     this.renderCellDelegate = this.renderCell.createDelegate(this);
40674     this.renderPropDelegate = this.renderProp.createDelegate(this);
40675 };
40676
40677 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
40678     
40679     
40680     nameText : 'Name',
40681     valueText : 'Value',
40682     
40683     dateFormat : 'm/j/Y',
40684     
40685     
40686     renderDate : function(dateVal){
40687         return dateVal.dateFormat(this.dateFormat);
40688     },
40689
40690     renderBool : function(bVal){
40691         return bVal ? 'true' : 'false';
40692     },
40693
40694     isCellEditable : function(colIndex, rowIndex){
40695         return colIndex == 1;
40696     },
40697
40698     getRenderer : function(col){
40699         return col == 1 ?
40700             this.renderCellDelegate : this.renderPropDelegate;
40701     },
40702
40703     renderProp : function(v){
40704         return this.getPropertyName(v);
40705     },
40706
40707     renderCell : function(val){
40708         var rv = val;
40709         if(val instanceof Date){
40710             rv = this.renderDate(val);
40711         }else if(typeof val == 'boolean'){
40712             rv = this.renderBool(val);
40713         }
40714         return Roo.util.Format.htmlEncode(rv);
40715     },
40716
40717     getPropertyName : function(name){
40718         var pn = this.grid.propertyNames;
40719         return pn && pn[name] ? pn[name] : name;
40720     },
40721
40722     getCellEditor : function(colIndex, rowIndex){
40723         var p = this.store.getProperty(rowIndex);
40724         var n = p.data['name'], val = p.data['value'];
40725         
40726         if(typeof(this.grid.customEditors[n]) == 'string'){
40727             return this.editors[this.grid.customEditors[n]];
40728         }
40729         if(typeof(this.grid.customEditors[n]) != 'undefined'){
40730             return this.grid.customEditors[n];
40731         }
40732         if(val instanceof Date){
40733             return this.editors['date'];
40734         }else if(typeof val == 'number'){
40735             return this.editors['number'];
40736         }else if(typeof val == 'boolean'){
40737             return this.editors['boolean'];
40738         }else{
40739             return this.editors['string'];
40740         }
40741     }
40742 });
40743
40744 /**
40745  * @class Roo.grid.PropertyGrid
40746  * @extends Roo.grid.EditorGrid
40747  * This class represents the  interface of a component based property grid control.
40748  * <br><br>Usage:<pre><code>
40749  var grid = new Roo.grid.PropertyGrid("my-container-id", {
40750       
40751  });
40752  // set any options
40753  grid.render();
40754  * </code></pre>
40755   
40756  * @constructor
40757  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40758  * The container MUST have some type of size defined for the grid to fill. The container will be
40759  * automatically set to position relative if it isn't already.
40760  * @param {Object} config A config object that sets properties on this grid.
40761  */
40762 Roo.grid.PropertyGrid = function(container, config){
40763     config = config || {};
40764     var store = new Roo.grid.PropertyStore(this);
40765     this.store = store;
40766     var cm = new Roo.grid.PropertyColumnModel(this, store);
40767     store.store.sort('name', 'ASC');
40768     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
40769         ds: store.store,
40770         cm: cm,
40771         enableColLock:false,
40772         enableColumnMove:false,
40773         stripeRows:false,
40774         trackMouseOver: false,
40775         clicksToEdit:1
40776     }, config));
40777     this.getGridEl().addClass('x-props-grid');
40778     this.lastEditRow = null;
40779     this.on('columnresize', this.onColumnResize, this);
40780     this.addEvents({
40781          /**
40782              * @event beforepropertychange
40783              * Fires before a property changes (return false to stop?)
40784              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40785              * @param {String} id Record Id
40786              * @param {String} newval New Value
40787          * @param {String} oldval Old Value
40788              */
40789         "beforepropertychange": true,
40790         /**
40791              * @event propertychange
40792              * Fires after a property changes
40793              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40794              * @param {String} id Record Id
40795              * @param {String} newval New Value
40796          * @param {String} oldval Old Value
40797              */
40798         "propertychange": true
40799     });
40800     this.customEditors = this.customEditors || {};
40801 };
40802 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
40803     
40804      /**
40805      * @cfg {Object} customEditors map of colnames=> custom editors.
40806      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
40807      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
40808      * false disables editing of the field.
40809          */
40810     
40811       /**
40812      * @cfg {Object} propertyNames map of property Names to their displayed value
40813          */
40814     
40815     render : function(){
40816         Roo.grid.PropertyGrid.superclass.render.call(this);
40817         this.autoSize.defer(100, this);
40818     },
40819
40820     autoSize : function(){
40821         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
40822         if(this.view){
40823             this.view.fitColumns();
40824         }
40825     },
40826
40827     onColumnResize : function(){
40828         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
40829         this.autoSize();
40830     },
40831     /**
40832      * Sets the data for the Grid
40833      * accepts a Key => Value object of all the elements avaiable.
40834      * @param {Object} data  to appear in grid.
40835      */
40836     setSource : function(source){
40837         this.store.setSource(source);
40838         //this.autoSize();
40839     },
40840     /**
40841      * Gets all the data from the grid.
40842      * @return {Object} data  data stored in grid
40843      */
40844     getSource : function(){
40845         return this.store.getSource();
40846     }
40847 });/*
40848   
40849  * Licence LGPL
40850  
40851  */
40852  
40853 /**
40854  * @class Roo.grid.Calendar
40855  * @extends Roo.util.Grid
40856  * This class extends the Grid to provide a calendar widget
40857  * <br><br>Usage:<pre><code>
40858  var grid = new Roo.grid.Calendar("my-container-id", {
40859      ds: myDataStore,
40860      cm: myColModel,
40861      selModel: mySelectionModel,
40862      autoSizeColumns: true,
40863      monitorWindowResize: false,
40864      trackMouseOver: true
40865      eventstore : real data store..
40866  });
40867  // set any options
40868  grid.render();
40869   
40870   * @constructor
40871  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40872  * The container MUST have some type of size defined for the grid to fill. The container will be
40873  * automatically set to position relative if it isn't already.
40874  * @param {Object} config A config object that sets properties on this grid.
40875  */
40876 Roo.grid.Calendar = function(container, config){
40877         // initialize the container
40878         this.container = Roo.get(container);
40879         this.container.update("");
40880         this.container.setStyle("overflow", "hidden");
40881     this.container.addClass('x-grid-container');
40882
40883     this.id = this.container.id;
40884
40885     Roo.apply(this, config);
40886     // check and correct shorthanded configs
40887     
40888     var rows = [];
40889     var d =1;
40890     for (var r = 0;r < 6;r++) {
40891         
40892         rows[r]=[];
40893         for (var c =0;c < 7;c++) {
40894             rows[r][c]= '';
40895         }
40896     }
40897     if (this.eventStore) {
40898         this.eventStore= Roo.factory(this.eventStore, Roo.data);
40899         this.eventStore.on('load',this.onLoad, this);
40900         this.eventStore.on('beforeload',this.clearEvents, this);
40901          
40902     }
40903     
40904     this.dataSource = new Roo.data.Store({
40905             proxy: new Roo.data.MemoryProxy(rows),
40906             reader: new Roo.data.ArrayReader({}, [
40907                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
40908     });
40909
40910     this.dataSource.load();
40911     this.ds = this.dataSource;
40912     this.ds.xmodule = this.xmodule || false;
40913     
40914     
40915     var cellRender = function(v,x,r)
40916     {
40917         return String.format(
40918             '<div class="fc-day  fc-widget-content"><div>' +
40919                 '<div class="fc-event-container"></div>' +
40920                 '<div class="fc-day-number">{0}</div>'+
40921                 
40922                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
40923             '</div></div>', v);
40924     
40925     }
40926     
40927     
40928     this.colModel = new Roo.grid.ColumnModel( [
40929         {
40930             xtype: 'ColumnModel',
40931             xns: Roo.grid,
40932             dataIndex : 'weekday0',
40933             header : 'Sunday',
40934             renderer : cellRender
40935         },
40936         {
40937             xtype: 'ColumnModel',
40938             xns: Roo.grid,
40939             dataIndex : 'weekday1',
40940             header : 'Monday',
40941             renderer : cellRender
40942         },
40943         {
40944             xtype: 'ColumnModel',
40945             xns: Roo.grid,
40946             dataIndex : 'weekday2',
40947             header : 'Tuesday',
40948             renderer : cellRender
40949         },
40950         {
40951             xtype: 'ColumnModel',
40952             xns: Roo.grid,
40953             dataIndex : 'weekday3',
40954             header : 'Wednesday',
40955             renderer : cellRender
40956         },
40957         {
40958             xtype: 'ColumnModel',
40959             xns: Roo.grid,
40960             dataIndex : 'weekday4',
40961             header : 'Thursday',
40962             renderer : cellRender
40963         },
40964         {
40965             xtype: 'ColumnModel',
40966             xns: Roo.grid,
40967             dataIndex : 'weekday5',
40968             header : 'Friday',
40969             renderer : cellRender
40970         },
40971         {
40972             xtype: 'ColumnModel',
40973             xns: Roo.grid,
40974             dataIndex : 'weekday6',
40975             header : 'Saturday',
40976             renderer : cellRender
40977         }
40978     ]);
40979     this.cm = this.colModel;
40980     this.cm.xmodule = this.xmodule || false;
40981  
40982         
40983           
40984     //this.selModel = new Roo.grid.CellSelectionModel();
40985     //this.sm = this.selModel;
40986     //this.selModel.init(this);
40987     
40988     
40989     if(this.width){
40990         this.container.setWidth(this.width);
40991     }
40992
40993     if(this.height){
40994         this.container.setHeight(this.height);
40995     }
40996     /** @private */
40997         this.addEvents({
40998         // raw events
40999         /**
41000          * @event click
41001          * The raw click event for the entire grid.
41002          * @param {Roo.EventObject} e
41003          */
41004         "click" : true,
41005         /**
41006          * @event dblclick
41007          * The raw dblclick event for the entire grid.
41008          * @param {Roo.EventObject} e
41009          */
41010         "dblclick" : true,
41011         /**
41012          * @event contextmenu
41013          * The raw contextmenu event for the entire grid.
41014          * @param {Roo.EventObject} e
41015          */
41016         "contextmenu" : true,
41017         /**
41018          * @event mousedown
41019          * The raw mousedown event for the entire grid.
41020          * @param {Roo.EventObject} e
41021          */
41022         "mousedown" : true,
41023         /**
41024          * @event mouseup
41025          * The raw mouseup event for the entire grid.
41026          * @param {Roo.EventObject} e
41027          */
41028         "mouseup" : true,
41029         /**
41030          * @event mouseover
41031          * The raw mouseover event for the entire grid.
41032          * @param {Roo.EventObject} e
41033          */
41034         "mouseover" : true,
41035         /**
41036          * @event mouseout
41037          * The raw mouseout event for the entire grid.
41038          * @param {Roo.EventObject} e
41039          */
41040         "mouseout" : true,
41041         /**
41042          * @event keypress
41043          * The raw keypress event for the entire grid.
41044          * @param {Roo.EventObject} e
41045          */
41046         "keypress" : true,
41047         /**
41048          * @event keydown
41049          * The raw keydown event for the entire grid.
41050          * @param {Roo.EventObject} e
41051          */
41052         "keydown" : true,
41053
41054         // custom events
41055
41056         /**
41057          * @event cellclick
41058          * Fires when a cell is clicked
41059          * @param {Grid} this
41060          * @param {Number} rowIndex
41061          * @param {Number} columnIndex
41062          * @param {Roo.EventObject} e
41063          */
41064         "cellclick" : true,
41065         /**
41066          * @event celldblclick
41067          * Fires when a cell is double clicked
41068          * @param {Grid} this
41069          * @param {Number} rowIndex
41070          * @param {Number} columnIndex
41071          * @param {Roo.EventObject} e
41072          */
41073         "celldblclick" : true,
41074         /**
41075          * @event rowclick
41076          * Fires when a row is clicked
41077          * @param {Grid} this
41078          * @param {Number} rowIndex
41079          * @param {Roo.EventObject} e
41080          */
41081         "rowclick" : true,
41082         /**
41083          * @event rowdblclick
41084          * Fires when a row is double clicked
41085          * @param {Grid} this
41086          * @param {Number} rowIndex
41087          * @param {Roo.EventObject} e
41088          */
41089         "rowdblclick" : true,
41090         /**
41091          * @event headerclick
41092          * Fires when a header is clicked
41093          * @param {Grid} this
41094          * @param {Number} columnIndex
41095          * @param {Roo.EventObject} e
41096          */
41097         "headerclick" : true,
41098         /**
41099          * @event headerdblclick
41100          * Fires when a header cell is double clicked
41101          * @param {Grid} this
41102          * @param {Number} columnIndex
41103          * @param {Roo.EventObject} e
41104          */
41105         "headerdblclick" : true,
41106         /**
41107          * @event rowcontextmenu
41108          * Fires when a row is right clicked
41109          * @param {Grid} this
41110          * @param {Number} rowIndex
41111          * @param {Roo.EventObject} e
41112          */
41113         "rowcontextmenu" : true,
41114         /**
41115          * @event cellcontextmenu
41116          * Fires when a cell is right clicked
41117          * @param {Grid} this
41118          * @param {Number} rowIndex
41119          * @param {Number} cellIndex
41120          * @param {Roo.EventObject} e
41121          */
41122          "cellcontextmenu" : true,
41123         /**
41124          * @event headercontextmenu
41125          * Fires when a header is right clicked
41126          * @param {Grid} this
41127          * @param {Number} columnIndex
41128          * @param {Roo.EventObject} e
41129          */
41130         "headercontextmenu" : true,
41131         /**
41132          * @event bodyscroll
41133          * Fires when the body element is scrolled
41134          * @param {Number} scrollLeft
41135          * @param {Number} scrollTop
41136          */
41137         "bodyscroll" : true,
41138         /**
41139          * @event columnresize
41140          * Fires when the user resizes a column
41141          * @param {Number} columnIndex
41142          * @param {Number} newSize
41143          */
41144         "columnresize" : true,
41145         /**
41146          * @event columnmove
41147          * Fires when the user moves a column
41148          * @param {Number} oldIndex
41149          * @param {Number} newIndex
41150          */
41151         "columnmove" : true,
41152         /**
41153          * @event startdrag
41154          * Fires when row(s) start being dragged
41155          * @param {Grid} this
41156          * @param {Roo.GridDD} dd The drag drop object
41157          * @param {event} e The raw browser event
41158          */
41159         "startdrag" : true,
41160         /**
41161          * @event enddrag
41162          * Fires when a drag operation is complete
41163          * @param {Grid} this
41164          * @param {Roo.GridDD} dd The drag drop object
41165          * @param {event} e The raw browser event
41166          */
41167         "enddrag" : true,
41168         /**
41169          * @event dragdrop
41170          * Fires when dragged row(s) are dropped on a valid DD target
41171          * @param {Grid} this
41172          * @param {Roo.GridDD} dd The drag drop object
41173          * @param {String} targetId The target drag drop object
41174          * @param {event} e The raw browser event
41175          */
41176         "dragdrop" : true,
41177         /**
41178          * @event dragover
41179          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
41180          * @param {Grid} this
41181          * @param {Roo.GridDD} dd The drag drop object
41182          * @param {String} targetId The target drag drop object
41183          * @param {event} e The raw browser event
41184          */
41185         "dragover" : true,
41186         /**
41187          * @event dragenter
41188          *  Fires when the dragged row(s) first cross another DD target while being dragged
41189          * @param {Grid} this
41190          * @param {Roo.GridDD} dd The drag drop object
41191          * @param {String} targetId The target drag drop object
41192          * @param {event} e The raw browser event
41193          */
41194         "dragenter" : true,
41195         /**
41196          * @event dragout
41197          * Fires when the dragged row(s) leave another DD target while being dragged
41198          * @param {Grid} this
41199          * @param {Roo.GridDD} dd The drag drop object
41200          * @param {String} targetId The target drag drop object
41201          * @param {event} e The raw browser event
41202          */
41203         "dragout" : true,
41204         /**
41205          * @event rowclass
41206          * Fires when a row is rendered, so you can change add a style to it.
41207          * @param {GridView} gridview   The grid view
41208          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
41209          */
41210         'rowclass' : true,
41211
41212         /**
41213          * @event render
41214          * Fires when the grid is rendered
41215          * @param {Grid} grid
41216          */
41217         'render' : true,
41218             /**
41219              * @event select
41220              * Fires when a date is selected
41221              * @param {DatePicker} this
41222              * @param {Date} date The selected date
41223              */
41224         'select': true,
41225         /**
41226              * @event monthchange
41227              * Fires when the displayed month changes 
41228              * @param {DatePicker} this
41229              * @param {Date} date The selected month
41230              */
41231         'monthchange': true,
41232         /**
41233              * @event evententer
41234              * Fires when mouse over an event
41235              * @param {Calendar} this
41236              * @param {event} Event
41237              */
41238         'evententer': true,
41239         /**
41240              * @event eventleave
41241              * Fires when the mouse leaves an
41242              * @param {Calendar} this
41243              * @param {event}
41244              */
41245         'eventleave': true,
41246         /**
41247              * @event eventclick
41248              * Fires when the mouse click an
41249              * @param {Calendar} this
41250              * @param {event}
41251              */
41252         'eventclick': true,
41253         /**
41254              * @event eventrender
41255              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
41256              * @param {Calendar} this
41257              * @param {data} data to be modified
41258              */
41259         'eventrender': true
41260         
41261     });
41262
41263     Roo.grid.Grid.superclass.constructor.call(this);
41264     this.on('render', function() {
41265         this.view.el.addClass('x-grid-cal'); 
41266         
41267         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
41268
41269     },this);
41270     
41271     if (!Roo.grid.Calendar.style) {
41272         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
41273             
41274             
41275             '.x-grid-cal .x-grid-col' :  {
41276                 height: 'auto !important',
41277                 'vertical-align': 'top'
41278             },
41279             '.x-grid-cal  .fc-event-hori' : {
41280                 height: '14px'
41281             }
41282              
41283             
41284         }, Roo.id());
41285     }
41286
41287     
41288     
41289 };
41290 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
41291     /**
41292      * @cfg {Store} eventStore The store that loads events.
41293      */
41294     eventStore : 25,
41295
41296      
41297     activeDate : false,
41298     startDay : 0,
41299     autoWidth : true,
41300     monitorWindowResize : false,
41301
41302     
41303     resizeColumns : function() {
41304         var col = (this.view.el.getWidth() / 7) - 3;
41305         // loop through cols, and setWidth
41306         for(var i =0 ; i < 7 ; i++){
41307             this.cm.setColumnWidth(i, col);
41308         }
41309     },
41310      setDate :function(date) {
41311         
41312         Roo.log('setDate?');
41313         
41314         this.resizeColumns();
41315         var vd = this.activeDate;
41316         this.activeDate = date;
41317 //        if(vd && this.el){
41318 //            var t = date.getTime();
41319 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
41320 //                Roo.log('using add remove');
41321 //                
41322 //                this.fireEvent('monthchange', this, date);
41323 //                
41324 //                this.cells.removeClass("fc-state-highlight");
41325 //                this.cells.each(function(c){
41326 //                   if(c.dateValue == t){
41327 //                       c.addClass("fc-state-highlight");
41328 //                       setTimeout(function(){
41329 //                            try{c.dom.firstChild.focus();}catch(e){}
41330 //                       }, 50);
41331 //                       return false;
41332 //                   }
41333 //                   return true;
41334 //                });
41335 //                return;
41336 //            }
41337 //        }
41338         
41339         var days = date.getDaysInMonth();
41340         
41341         var firstOfMonth = date.getFirstDateOfMonth();
41342         var startingPos = firstOfMonth.getDay()-this.startDay;
41343         
41344         if(startingPos < this.startDay){
41345             startingPos += 7;
41346         }
41347         
41348         var pm = date.add(Date.MONTH, -1);
41349         var prevStart = pm.getDaysInMonth()-startingPos;
41350 //        
41351         
41352         
41353         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
41354         
41355         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
41356         //this.cells.addClassOnOver('fc-state-hover');
41357         
41358         var cells = this.cells.elements;
41359         var textEls = this.textNodes;
41360         
41361         //Roo.each(cells, function(cell){
41362         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
41363         //});
41364         
41365         days += startingPos;
41366
41367         // convert everything to numbers so it's fast
41368         var day = 86400000;
41369         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
41370         //Roo.log(d);
41371         //Roo.log(pm);
41372         //Roo.log(prevStart);
41373         
41374         var today = new Date().clearTime().getTime();
41375         var sel = date.clearTime().getTime();
41376         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
41377         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
41378         var ddMatch = this.disabledDatesRE;
41379         var ddText = this.disabledDatesText;
41380         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
41381         var ddaysText = this.disabledDaysText;
41382         var format = this.format;
41383         
41384         var setCellClass = function(cal, cell){
41385             
41386             //Roo.log('set Cell Class');
41387             cell.title = "";
41388             var t = d.getTime();
41389             
41390             //Roo.log(d);
41391             
41392             
41393             cell.dateValue = t;
41394             if(t == today){
41395                 cell.className += " fc-today";
41396                 cell.className += " fc-state-highlight";
41397                 cell.title = cal.todayText;
41398             }
41399             if(t == sel){
41400                 // disable highlight in other month..
41401                 cell.className += " fc-state-highlight";
41402                 
41403             }
41404             // disabling
41405             if(t < min) {
41406                 //cell.className = " fc-state-disabled";
41407                 cell.title = cal.minText;
41408                 return;
41409             }
41410             if(t > max) {
41411                 //cell.className = " fc-state-disabled";
41412                 cell.title = cal.maxText;
41413                 return;
41414             }
41415             if(ddays){
41416                 if(ddays.indexOf(d.getDay()) != -1){
41417                     // cell.title = ddaysText;
41418                    // cell.className = " fc-state-disabled";
41419                 }
41420             }
41421             if(ddMatch && format){
41422                 var fvalue = d.dateFormat(format);
41423                 if(ddMatch.test(fvalue)){
41424                     cell.title = ddText.replace("%0", fvalue);
41425                    cell.className = " fc-state-disabled";
41426                 }
41427             }
41428             
41429             if (!cell.initialClassName) {
41430                 cell.initialClassName = cell.dom.className;
41431             }
41432             
41433             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
41434         };
41435
41436         var i = 0;
41437         
41438         for(; i < startingPos; i++) {
41439             cells[i].dayName =  (++prevStart);
41440             Roo.log(textEls[i]);
41441             d.setDate(d.getDate()+1);
41442             
41443             //cells[i].className = "fc-past fc-other-month";
41444             setCellClass(this, cells[i]);
41445         }
41446         
41447         var intDay = 0;
41448         
41449         for(; i < days; i++){
41450             intDay = i - startingPos + 1;
41451             cells[i].dayName =  (intDay);
41452             d.setDate(d.getDate()+1);
41453             
41454             cells[i].className = ''; // "x-date-active";
41455             setCellClass(this, cells[i]);
41456         }
41457         var extraDays = 0;
41458         
41459         for(; i < 42; i++) {
41460             //textEls[i].innerHTML = (++extraDays);
41461             
41462             d.setDate(d.getDate()+1);
41463             cells[i].dayName = (++extraDays);
41464             cells[i].className = "fc-future fc-other-month";
41465             setCellClass(this, cells[i]);
41466         }
41467         
41468         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
41469         
41470         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
41471         
41472         // this will cause all the cells to mis
41473         var rows= [];
41474         var i =0;
41475         for (var r = 0;r < 6;r++) {
41476             for (var c =0;c < 7;c++) {
41477                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
41478             }    
41479         }
41480         
41481         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
41482         for(i=0;i<cells.length;i++) {
41483             
41484             this.cells.elements[i].dayName = cells[i].dayName ;
41485             this.cells.elements[i].className = cells[i].className;
41486             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
41487             this.cells.elements[i].title = cells[i].title ;
41488             this.cells.elements[i].dateValue = cells[i].dateValue ;
41489         }
41490         
41491         
41492         
41493         
41494         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
41495         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
41496         
41497         ////if(totalRows != 6){
41498             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
41499            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
41500        // }
41501         
41502         this.fireEvent('monthchange', this, date);
41503         
41504         
41505     },
41506  /**
41507      * Returns the grid's SelectionModel.
41508      * @return {SelectionModel}
41509      */
41510     getSelectionModel : function(){
41511         if(!this.selModel){
41512             this.selModel = new Roo.grid.CellSelectionModel();
41513         }
41514         return this.selModel;
41515     },
41516
41517     load: function() {
41518         this.eventStore.load()
41519         
41520         
41521         
41522     },
41523     
41524     findCell : function(dt) {
41525         dt = dt.clearTime().getTime();
41526         var ret = false;
41527         this.cells.each(function(c){
41528             //Roo.log("check " +c.dateValue + '?=' + dt);
41529             if(c.dateValue == dt){
41530                 ret = c;
41531                 return false;
41532             }
41533             return true;
41534         });
41535         
41536         return ret;
41537     },
41538     
41539     findCells : function(rec) {
41540         var s = rec.data.start_dt.clone().clearTime().getTime();
41541        // Roo.log(s);
41542         var e= rec.data.end_dt.clone().clearTime().getTime();
41543        // Roo.log(e);
41544         var ret = [];
41545         this.cells.each(function(c){
41546              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
41547             
41548             if(c.dateValue > e){
41549                 return ;
41550             }
41551             if(c.dateValue < s){
41552                 return ;
41553             }
41554             ret.push(c);
41555         });
41556         
41557         return ret;    
41558     },
41559     
41560     findBestRow: function(cells)
41561     {
41562         var ret = 0;
41563         
41564         for (var i =0 ; i < cells.length;i++) {
41565             ret  = Math.max(cells[i].rows || 0,ret);
41566         }
41567         return ret;
41568         
41569     },
41570     
41571     
41572     addItem : function(rec)
41573     {
41574         // look for vertical location slot in
41575         var cells = this.findCells(rec);
41576         
41577         rec.row = this.findBestRow(cells);
41578         
41579         // work out the location.
41580         
41581         var crow = false;
41582         var rows = [];
41583         for(var i =0; i < cells.length; i++) {
41584             if (!crow) {
41585                 crow = {
41586                     start : cells[i],
41587                     end :  cells[i]
41588                 };
41589                 continue;
41590             }
41591             if (crow.start.getY() == cells[i].getY()) {
41592                 // on same row.
41593                 crow.end = cells[i];
41594                 continue;
41595             }
41596             // different row.
41597             rows.push(crow);
41598             crow = {
41599                 start: cells[i],
41600                 end : cells[i]
41601             };
41602             
41603         }
41604         
41605         rows.push(crow);
41606         rec.els = [];
41607         rec.rows = rows;
41608         rec.cells = cells;
41609         for (var i = 0; i < cells.length;i++) {
41610             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
41611             
41612         }
41613         
41614         
41615     },
41616     
41617     clearEvents: function() {
41618         
41619         if (!this.eventStore.getCount()) {
41620             return;
41621         }
41622         // reset number of rows in cells.
41623         Roo.each(this.cells.elements, function(c){
41624             c.rows = 0;
41625         });
41626         
41627         this.eventStore.each(function(e) {
41628             this.clearEvent(e);
41629         },this);
41630         
41631     },
41632     
41633     clearEvent : function(ev)
41634     {
41635         if (ev.els) {
41636             Roo.each(ev.els, function(el) {
41637                 el.un('mouseenter' ,this.onEventEnter, this);
41638                 el.un('mouseleave' ,this.onEventLeave, this);
41639                 el.remove();
41640             },this);
41641             ev.els = [];
41642         }
41643     },
41644     
41645     
41646     renderEvent : function(ev,ctr) {
41647         if (!ctr) {
41648              ctr = this.view.el.select('.fc-event-container',true).first();
41649         }
41650         
41651          
41652         this.clearEvent(ev);
41653             //code
41654        
41655         
41656         
41657         ev.els = [];
41658         var cells = ev.cells;
41659         var rows = ev.rows;
41660         this.fireEvent('eventrender', this, ev);
41661         
41662         for(var i =0; i < rows.length; i++) {
41663             
41664             cls = '';
41665             if (i == 0) {
41666                 cls += ' fc-event-start';
41667             }
41668             if ((i+1) == rows.length) {
41669                 cls += ' fc-event-end';
41670             }
41671             
41672             //Roo.log(ev.data);
41673             // how many rows should it span..
41674             var cg = this.eventTmpl.append(ctr,Roo.apply({
41675                 fccls : cls
41676                 
41677             }, ev.data) , true);
41678             
41679             
41680             cg.on('mouseenter' ,this.onEventEnter, this, ev);
41681             cg.on('mouseleave' ,this.onEventLeave, this, ev);
41682             cg.on('click', this.onEventClick, this, ev);
41683             
41684             ev.els.push(cg);
41685             
41686             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
41687             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
41688             //Roo.log(cg);
41689              
41690             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
41691             cg.setWidth(ebox.right - sbox.x -2);
41692         }
41693     },
41694     
41695     renderEvents: function()
41696     {   
41697         // first make sure there is enough space..
41698         
41699         if (!this.eventTmpl) {
41700             this.eventTmpl = new Roo.Template(
41701                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
41702                     '<div class="fc-event-inner">' +
41703                         '<span class="fc-event-time">{time}</span>' +
41704                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
41705                     '</div>' +
41706                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
41707                 '</div>'
41708             );
41709                 
41710         }
41711                
41712         
41713         
41714         this.cells.each(function(c) {
41715             //Roo.log(c.select('.fc-day-content div',true).first());
41716             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
41717         });
41718         
41719         var ctr = this.view.el.select('.fc-event-container',true).first();
41720         
41721         var cls;
41722         this.eventStore.each(function(ev){
41723             
41724             this.renderEvent(ev);
41725              
41726              
41727         }, this);
41728         this.view.layout();
41729         
41730     },
41731     
41732     onEventEnter: function (e, el,event,d) {
41733         this.fireEvent('evententer', this, el, event);
41734     },
41735     
41736     onEventLeave: function (e, el,event,d) {
41737         this.fireEvent('eventleave', this, el, event);
41738     },
41739     
41740     onEventClick: function (e, el,event,d) {
41741         this.fireEvent('eventclick', this, el, event);
41742     },
41743     
41744     onMonthChange: function () {
41745         this.store.load();
41746     },
41747     
41748     onLoad: function () {
41749         
41750         //Roo.log('calendar onload');
41751 //         
41752         if(this.eventStore.getCount() > 0){
41753             
41754            
41755             
41756             this.eventStore.each(function(d){
41757                 
41758                 
41759                 // FIXME..
41760                 var add =   d.data;
41761                 if (typeof(add.end_dt) == 'undefined')  {
41762                     Roo.log("Missing End time in calendar data: ");
41763                     Roo.log(d);
41764                     return;
41765                 }
41766                 if (typeof(add.start_dt) == 'undefined')  {
41767                     Roo.log("Missing Start time in calendar data: ");
41768                     Roo.log(d);
41769                     return;
41770                 }
41771                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
41772                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
41773                 add.id = add.id || d.id;
41774                 add.title = add.title || '??';
41775                 
41776                 this.addItem(d);
41777                 
41778              
41779             },this);
41780         }
41781         
41782         this.renderEvents();
41783     }
41784     
41785
41786 });
41787 /*
41788  grid : {
41789                 xtype: 'Grid',
41790                 xns: Roo.grid,
41791                 listeners : {
41792                     render : function ()
41793                     {
41794                         _this.grid = this;
41795                         
41796                         if (!this.view.el.hasClass('course-timesheet')) {
41797                             this.view.el.addClass('course-timesheet');
41798                         }
41799                         if (this.tsStyle) {
41800                             this.ds.load({});
41801                             return; 
41802                         }
41803                         Roo.log('width');
41804                         Roo.log(_this.grid.view.el.getWidth());
41805                         
41806                         
41807                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
41808                             '.course-timesheet .x-grid-row' : {
41809                                 height: '80px'
41810                             },
41811                             '.x-grid-row td' : {
41812                                 'vertical-align' : 0
41813                             },
41814                             '.course-edit-link' : {
41815                                 'color' : 'blue',
41816                                 'text-overflow' : 'ellipsis',
41817                                 'overflow' : 'hidden',
41818                                 'white-space' : 'nowrap',
41819                                 'cursor' : 'pointer'
41820                             },
41821                             '.sub-link' : {
41822                                 'color' : 'green'
41823                             },
41824                             '.de-act-sup-link' : {
41825                                 'color' : 'purple',
41826                                 'text-decoration' : 'line-through'
41827                             },
41828                             '.de-act-link' : {
41829                                 'color' : 'red',
41830                                 'text-decoration' : 'line-through'
41831                             },
41832                             '.course-timesheet .course-highlight' : {
41833                                 'border-top-style': 'dashed !important',
41834                                 'border-bottom-bottom': 'dashed !important'
41835                             },
41836                             '.course-timesheet .course-item' : {
41837                                 'font-family'   : 'tahoma, arial, helvetica',
41838                                 'font-size'     : '11px',
41839                                 'overflow'      : 'hidden',
41840                                 'padding-left'  : '10px',
41841                                 'padding-right' : '10px',
41842                                 'padding-top' : '10px' 
41843                             }
41844                             
41845                         }, Roo.id());
41846                                 this.ds.load({});
41847                     }
41848                 },
41849                 autoWidth : true,
41850                 monitorWindowResize : false,
41851                 cellrenderer : function(v,x,r)
41852                 {
41853                     return v;
41854                 },
41855                 sm : {
41856                     xtype: 'CellSelectionModel',
41857                     xns: Roo.grid
41858                 },
41859                 dataSource : {
41860                     xtype: 'Store',
41861                     xns: Roo.data,
41862                     listeners : {
41863                         beforeload : function (_self, options)
41864                         {
41865                             options.params = options.params || {};
41866                             options.params._month = _this.monthField.getValue();
41867                             options.params.limit = 9999;
41868                             options.params['sort'] = 'when_dt';    
41869                             options.params['dir'] = 'ASC';    
41870                             this.proxy.loadResponse = this.loadResponse;
41871                             Roo.log("load?");
41872                             //this.addColumns();
41873                         },
41874                         load : function (_self, records, options)
41875                         {
41876                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
41877                                 // if you click on the translation.. you can edit it...
41878                                 var el = Roo.get(this);
41879                                 var id = el.dom.getAttribute('data-id');
41880                                 var d = el.dom.getAttribute('data-date');
41881                                 var t = el.dom.getAttribute('data-time');
41882                                 //var id = this.child('span').dom.textContent;
41883                                 
41884                                 //Roo.log(this);
41885                                 Pman.Dialog.CourseCalendar.show({
41886                                     id : id,
41887                                     when_d : d,
41888                                     when_t : t,
41889                                     productitem_active : id ? 1 : 0
41890                                 }, function() {
41891                                     _this.grid.ds.load({});
41892                                 });
41893                            
41894                            });
41895                            
41896                            _this.panel.fireEvent('resize', [ '', '' ]);
41897                         }
41898                     },
41899                     loadResponse : function(o, success, response){
41900                             // this is overridden on before load..
41901                             
41902                             Roo.log("our code?");       
41903                             //Roo.log(success);
41904                             //Roo.log(response)
41905                             delete this.activeRequest;
41906                             if(!success){
41907                                 this.fireEvent("loadexception", this, o, response);
41908                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41909                                 return;
41910                             }
41911                             var result;
41912                             try {
41913                                 result = o.reader.read(response);
41914                             }catch(e){
41915                                 Roo.log("load exception?");
41916                                 this.fireEvent("loadexception", this, o, response, e);
41917                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41918                                 return;
41919                             }
41920                             Roo.log("ready...");        
41921                             // loop through result.records;
41922                             // and set this.tdate[date] = [] << array of records..
41923                             _this.tdata  = {};
41924                             Roo.each(result.records, function(r){
41925                                 //Roo.log(r.data);
41926                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
41927                                     _this.tdata[r.data.when_dt.format('j')] = [];
41928                                 }
41929                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
41930                             });
41931                             
41932                             //Roo.log(_this.tdata);
41933                             
41934                             result.records = [];
41935                             result.totalRecords = 6;
41936                     
41937                             // let's generate some duumy records for the rows.
41938                             //var st = _this.dateField.getValue();
41939                             
41940                             // work out monday..
41941                             //st = st.add(Date.DAY, -1 * st.format('w'));
41942                             
41943                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41944                             
41945                             var firstOfMonth = date.getFirstDayOfMonth();
41946                             var days = date.getDaysInMonth();
41947                             var d = 1;
41948                             var firstAdded = false;
41949                             for (var i = 0; i < result.totalRecords ; i++) {
41950                                 //var d= st.add(Date.DAY, i);
41951                                 var row = {};
41952                                 var added = 0;
41953                                 for(var w = 0 ; w < 7 ; w++){
41954                                     if(!firstAdded && firstOfMonth != w){
41955                                         continue;
41956                                     }
41957                                     if(d > days){
41958                                         continue;
41959                                     }
41960                                     firstAdded = true;
41961                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
41962                                     row['weekday'+w] = String.format(
41963                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
41964                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
41965                                                     d,
41966                                                     date.format('Y-m-')+dd
41967                                                 );
41968                                     added++;
41969                                     if(typeof(_this.tdata[d]) != 'undefined'){
41970                                         Roo.each(_this.tdata[d], function(r){
41971                                             var is_sub = '';
41972                                             var deactive = '';
41973                                             var id = r.id;
41974                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
41975                                             if(r.parent_id*1>0){
41976                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
41977                                                 id = r.parent_id;
41978                                             }
41979                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
41980                                                 deactive = 'de-act-link';
41981                                             }
41982                                             
41983                                             row['weekday'+w] += String.format(
41984                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
41985                                                     id, //0
41986                                                     r.product_id_name, //1
41987                                                     r.when_dt.format('h:ia'), //2
41988                                                     is_sub, //3
41989                                                     deactive, //4
41990                                                     desc // 5
41991                                             );
41992                                         });
41993                                     }
41994                                     d++;
41995                                 }
41996                                 
41997                                 // only do this if something added..
41998                                 if(added > 0){ 
41999                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
42000                                 }
42001                                 
42002                                 
42003                                 // push it twice. (second one with an hour..
42004                                 
42005                             }
42006                             //Roo.log(result);
42007                             this.fireEvent("load", this, o, o.request.arg);
42008                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
42009                         },
42010                     sortInfo : {field: 'when_dt', direction : 'ASC' },
42011                     proxy : {
42012                         xtype: 'HttpProxy',
42013                         xns: Roo.data,
42014                         method : 'GET',
42015                         url : baseURL + '/Roo/Shop_course.php'
42016                     },
42017                     reader : {
42018                         xtype: 'JsonReader',
42019                         xns: Roo.data,
42020                         id : 'id',
42021                         fields : [
42022                             {
42023                                 'name': 'id',
42024                                 'type': 'int'
42025                             },
42026                             {
42027                                 'name': 'when_dt',
42028                                 'type': 'string'
42029                             },
42030                             {
42031                                 'name': 'end_dt',
42032                                 'type': 'string'
42033                             },
42034                             {
42035                                 'name': 'parent_id',
42036                                 'type': 'int'
42037                             },
42038                             {
42039                                 'name': 'product_id',
42040                                 'type': 'int'
42041                             },
42042                             {
42043                                 'name': 'productitem_id',
42044                                 'type': 'int'
42045                             },
42046                             {
42047                                 'name': 'guid',
42048                                 'type': 'int'
42049                             }
42050                         ]
42051                     }
42052                 },
42053                 toolbar : {
42054                     xtype: 'Toolbar',
42055                     xns: Roo,
42056                     items : [
42057                         {
42058                             xtype: 'Button',
42059                             xns: Roo.Toolbar,
42060                             listeners : {
42061                                 click : function (_self, e)
42062                                 {
42063                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
42064                                     sd.setMonth(sd.getMonth()-1);
42065                                     _this.monthField.setValue(sd.format('Y-m-d'));
42066                                     _this.grid.ds.load({});
42067                                 }
42068                             },
42069                             text : "Back"
42070                         },
42071                         {
42072                             xtype: 'Separator',
42073                             xns: Roo.Toolbar
42074                         },
42075                         {
42076                             xtype: 'MonthField',
42077                             xns: Roo.form,
42078                             listeners : {
42079                                 render : function (_self)
42080                                 {
42081                                     _this.monthField = _self;
42082                                    // _this.monthField.set  today
42083                                 },
42084                                 select : function (combo, date)
42085                                 {
42086                                     _this.grid.ds.load({});
42087                                 }
42088                             },
42089                             value : (function() { return new Date(); })()
42090                         },
42091                         {
42092                             xtype: 'Separator',
42093                             xns: Roo.Toolbar
42094                         },
42095                         {
42096                             xtype: 'TextItem',
42097                             xns: Roo.Toolbar,
42098                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
42099                         },
42100                         {
42101                             xtype: 'Fill',
42102                             xns: Roo.Toolbar
42103                         },
42104                         {
42105                             xtype: 'Button',
42106                             xns: Roo.Toolbar,
42107                             listeners : {
42108                                 click : function (_self, e)
42109                                 {
42110                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
42111                                     sd.setMonth(sd.getMonth()+1);
42112                                     _this.monthField.setValue(sd.format('Y-m-d'));
42113                                     _this.grid.ds.load({});
42114                                 }
42115                             },
42116                             text : "Next"
42117                         }
42118                     ]
42119                 },
42120                  
42121             }
42122         };
42123         
42124         *//*
42125  * Based on:
42126  * Ext JS Library 1.1.1
42127  * Copyright(c) 2006-2007, Ext JS, LLC.
42128  *
42129  * Originally Released Under LGPL - original licence link has changed is not relivant.
42130  *
42131  * Fork - LGPL
42132  * <script type="text/javascript">
42133  */
42134  
42135 /**
42136  * @class Roo.LoadMask
42137  * A simple utility class for generically masking elements while loading data.  If the element being masked has
42138  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
42139  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
42140  * element's UpdateManager load indicator and will be destroyed after the initial load.
42141  * @constructor
42142  * Create a new LoadMask
42143  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
42144  * @param {Object} config The config object
42145  */
42146 Roo.LoadMask = function(el, config){
42147     this.el = Roo.get(el);
42148     Roo.apply(this, config);
42149     if(this.store){
42150         this.store.on('beforeload', this.onBeforeLoad, this);
42151         this.store.on('load', this.onLoad, this);
42152         this.store.on('loadexception', this.onLoadException, this);
42153         this.removeMask = false;
42154     }else{
42155         var um = this.el.getUpdateManager();
42156         um.showLoadIndicator = false; // disable the default indicator
42157         um.on('beforeupdate', this.onBeforeLoad, this);
42158         um.on('update', this.onLoad, this);
42159         um.on('failure', this.onLoad, this);
42160         this.removeMask = true;
42161     }
42162 };
42163
42164 Roo.LoadMask.prototype = {
42165     /**
42166      * @cfg {Boolean} removeMask
42167      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
42168      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
42169      */
42170     /**
42171      * @cfg {String} msg
42172      * The text to display in a centered loading message box (defaults to 'Loading...')
42173      */
42174     msg : 'Loading...',
42175     /**
42176      * @cfg {String} msgCls
42177      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
42178      */
42179     msgCls : 'x-mask-loading',
42180
42181     /**
42182      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
42183      * @type Boolean
42184      */
42185     disabled: false,
42186
42187     /**
42188      * Disables the mask to prevent it from being displayed
42189      */
42190     disable : function(){
42191        this.disabled = true;
42192     },
42193
42194     /**
42195      * Enables the mask so that it can be displayed
42196      */
42197     enable : function(){
42198         this.disabled = false;
42199     },
42200     
42201     onLoadException : function()
42202     {
42203         Roo.log(arguments);
42204         
42205         if (typeof(arguments[3]) != 'undefined') {
42206             Roo.MessageBox.alert("Error loading",arguments[3]);
42207         } 
42208         /*
42209         try {
42210             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
42211                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
42212             }   
42213         } catch(e) {
42214             
42215         }
42216         */
42217     
42218         
42219         
42220         this.el.unmask(this.removeMask);
42221     },
42222     // private
42223     onLoad : function()
42224     {
42225         this.el.unmask(this.removeMask);
42226     },
42227
42228     // private
42229     onBeforeLoad : function(){
42230         if(!this.disabled){
42231             this.el.mask(this.msg, this.msgCls);
42232         }
42233     },
42234
42235     // private
42236     destroy : function(){
42237         if(this.store){
42238             this.store.un('beforeload', this.onBeforeLoad, this);
42239             this.store.un('load', this.onLoad, this);
42240             this.store.un('loadexception', this.onLoadException, this);
42241         }else{
42242             var um = this.el.getUpdateManager();
42243             um.un('beforeupdate', this.onBeforeLoad, this);
42244             um.un('update', this.onLoad, this);
42245             um.un('failure', this.onLoad, this);
42246         }
42247     }
42248 };/*
42249  * Based on:
42250  * Ext JS Library 1.1.1
42251  * Copyright(c) 2006-2007, Ext JS, LLC.
42252  *
42253  * Originally Released Under LGPL - original licence link has changed is not relivant.
42254  *
42255  * Fork - LGPL
42256  * <script type="text/javascript">
42257  */
42258
42259
42260 /**
42261  * @class Roo.XTemplate
42262  * @extends Roo.Template
42263  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
42264 <pre><code>
42265 var t = new Roo.XTemplate(
42266         '&lt;select name="{name}"&gt;',
42267                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
42268         '&lt;/select&gt;'
42269 );
42270  
42271 // then append, applying the master template values
42272  </code></pre>
42273  *
42274  * Supported features:
42275  *
42276  *  Tags:
42277
42278 <pre><code>
42279       {a_variable} - output encoded.
42280       {a_variable.format:("Y-m-d")} - call a method on the variable
42281       {a_variable:raw} - unencoded output
42282       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
42283       {a_variable:this.method_on_template(...)} - call a method on the template object.
42284  
42285 </code></pre>
42286  *  The tpl tag:
42287 <pre><code>
42288         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
42289         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
42290         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
42291         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
42292   
42293         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
42294         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
42295 </code></pre>
42296  *      
42297  */
42298 Roo.XTemplate = function()
42299 {
42300     Roo.XTemplate.superclass.constructor.apply(this, arguments);
42301     if (this.html) {
42302         this.compile();
42303     }
42304 };
42305
42306
42307 Roo.extend(Roo.XTemplate, Roo.Template, {
42308
42309     /**
42310      * The various sub templates
42311      */
42312     tpls : false,
42313     /**
42314      *
42315      * basic tag replacing syntax
42316      * WORD:WORD()
42317      *
42318      * // you can fake an object call by doing this
42319      *  x.t:(test,tesT) 
42320      * 
42321      */
42322     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
42323
42324     /**
42325      * compile the template
42326      *
42327      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
42328      *
42329      */
42330     compile: function()
42331     {
42332         var s = this.html;
42333      
42334         s = ['<tpl>', s, '</tpl>'].join('');
42335     
42336         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
42337             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
42338             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
42339             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
42340             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
42341             m,
42342             id     = 0,
42343             tpls   = [];
42344     
42345         while(true == !!(m = s.match(re))){
42346             var forMatch   = m[0].match(nameRe),
42347                 ifMatch   = m[0].match(ifRe),
42348                 execMatch   = m[0].match(execRe),
42349                 namedMatch   = m[0].match(namedRe),
42350                 
42351                 exp  = null, 
42352                 fn   = null,
42353                 exec = null,
42354                 name = forMatch && forMatch[1] ? forMatch[1] : '';
42355                 
42356             if (ifMatch) {
42357                 // if - puts fn into test..
42358                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
42359                 if(exp){
42360                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
42361                 }
42362             }
42363             
42364             if (execMatch) {
42365                 // exec - calls a function... returns empty if true is  returned.
42366                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
42367                 if(exp){
42368                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
42369                 }
42370             }
42371             
42372             
42373             if (name) {
42374                 // for = 
42375                 switch(name){
42376                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
42377                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
42378                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
42379                 }
42380             }
42381             var uid = namedMatch ? namedMatch[1] : id;
42382             
42383             
42384             tpls.push({
42385                 id:     namedMatch ? namedMatch[1] : id,
42386                 target: name,
42387                 exec:   exec,
42388                 test:   fn,
42389                 body:   m[1] || ''
42390             });
42391             if (namedMatch) {
42392                 s = s.replace(m[0], '');
42393             } else { 
42394                 s = s.replace(m[0], '{xtpl'+ id + '}');
42395             }
42396             ++id;
42397         }
42398         this.tpls = [];
42399         for(var i = tpls.length-1; i >= 0; --i){
42400             this.compileTpl(tpls[i]);
42401             this.tpls[tpls[i].id] = tpls[i];
42402         }
42403         this.master = tpls[tpls.length-1];
42404         return this;
42405     },
42406     /**
42407      * same as applyTemplate, except it's done to one of the subTemplates
42408      * when using named templates, you can do:
42409      *
42410      * var str = pl.applySubTemplate('your-name', values);
42411      *
42412      * 
42413      * @param {Number} id of the template
42414      * @param {Object} values to apply to template
42415      * @param {Object} parent (normaly the instance of this object)
42416      */
42417     applySubTemplate : function(id, values, parent)
42418     {
42419         
42420         
42421         var t = this.tpls[id];
42422         
42423         
42424         try { 
42425             if(t.test && !t.test.call(this, values, parent)){
42426                 return '';
42427             }
42428         } catch(e) {
42429             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
42430             Roo.log(e.toString());
42431             Roo.log(t.test);
42432             return ''
42433         }
42434         try { 
42435             
42436             if(t.exec && t.exec.call(this, values, parent)){
42437                 return '';
42438             }
42439         } catch(e) {
42440             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
42441             Roo.log(e.toString());
42442             Roo.log(t.exec);
42443             return ''
42444         }
42445         try {
42446             var vs = t.target ? t.target.call(this, values, parent) : values;
42447             parent = t.target ? values : parent;
42448             if(t.target && vs instanceof Array){
42449                 var buf = [];
42450                 for(var i = 0, len = vs.length; i < len; i++){
42451                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
42452                 }
42453                 return buf.join('');
42454             }
42455             return t.compiled.call(this, vs, parent);
42456         } catch (e) {
42457             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
42458             Roo.log(e.toString());
42459             Roo.log(t.compiled);
42460             return '';
42461         }
42462     },
42463
42464     compileTpl : function(tpl)
42465     {
42466         var fm = Roo.util.Format;
42467         var useF = this.disableFormats !== true;
42468         var sep = Roo.isGecko ? "+" : ",";
42469         var undef = function(str) {
42470             Roo.log("Property not found :"  + str);
42471             return '';
42472         };
42473         
42474         var fn = function(m, name, format, args)
42475         {
42476             //Roo.log(arguments);
42477             args = args ? args.replace(/\\'/g,"'") : args;
42478             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
42479             if (typeof(format) == 'undefined') {
42480                 format= 'htmlEncode';
42481             }
42482             if (format == 'raw' ) {
42483                 format = false;
42484             }
42485             
42486             if(name.substr(0, 4) == 'xtpl'){
42487                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
42488             }
42489             
42490             // build an array of options to determine if value is undefined..
42491             
42492             // basically get 'xxxx.yyyy' then do
42493             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
42494             //    (function () { Roo.log("Property not found"); return ''; })() :
42495             //    ......
42496             
42497             var udef_ar = [];
42498             var lookfor = '';
42499             Roo.each(name.split('.'), function(st) {
42500                 lookfor += (lookfor.length ? '.': '') + st;
42501                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
42502             });
42503             
42504             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
42505             
42506             
42507             if(format && useF){
42508                 
42509                 args = args ? ',' + args : "";
42510                  
42511                 if(format.substr(0, 5) != "this."){
42512                     format = "fm." + format + '(';
42513                 }else{
42514                     format = 'this.call("'+ format.substr(5) + '", ';
42515                     args = ", values";
42516                 }
42517                 
42518                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
42519             }
42520              
42521             if (args.length) {
42522                 // called with xxyx.yuu:(test,test)
42523                 // change to ()
42524                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
42525             }
42526             // raw.. - :raw modifier..
42527             return "'"+ sep + udef_st  + name + ")"+sep+"'";
42528             
42529         };
42530         var body;
42531         // branched to use + in gecko and [].join() in others
42532         if(Roo.isGecko){
42533             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
42534                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
42535                     "';};};";
42536         }else{
42537             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
42538             body.push(tpl.body.replace(/(\r\n|\n)/g,
42539                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
42540             body.push("'].join('');};};");
42541             body = body.join('');
42542         }
42543         
42544         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
42545        
42546         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
42547         eval(body);
42548         
42549         return this;
42550     },
42551
42552     applyTemplate : function(values){
42553         return this.master.compiled.call(this, values, {});
42554         //var s = this.subs;
42555     },
42556
42557     apply : function(){
42558         return this.applyTemplate.apply(this, arguments);
42559     }
42560
42561  });
42562
42563 Roo.XTemplate.from = function(el){
42564     el = Roo.getDom(el);
42565     return new Roo.XTemplate(el.value || el.innerHTML);
42566 };