roojs-ui.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13
14 /*
15  * These classes are derivatives of the similarly named classes in the YUI Library.
16  * The original license:
17  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18  * Code licensed under the BSD License:
19  * http://developer.yahoo.net/yui/license.txt
20  */
21
22 (function() {
23
24 var Event=Roo.EventManager;
25 var Dom=Roo.lib.Dom;
26
27 /**
28  * @class Roo.dd.DragDrop
29  * @extends Roo.util.Observable
30  * Defines the interface and base operation of items that that can be
31  * dragged or can be drop targets.  It was designed to be extended, overriding
32  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
33  * Up to three html elements can be associated with a DragDrop instance:
34  * <ul>
35  * <li>linked element: the element that is passed into the constructor.
36  * This is the element which defines the boundaries for interaction with
37  * other DragDrop objects.</li>
38  * <li>handle element(s): The drag operation only occurs if the element that
39  * was clicked matches a handle element.  By default this is the linked
40  * element, but there are times that you will want only a portion of the
41  * linked element to initiate the drag operation, and the setHandleElId()
42  * method provides a way to define this.</li>
43  * <li>drag element: this represents the element that would be moved along
44  * with the cursor during a drag operation.  By default, this is the linked
45  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
46  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
47  * </li>
48  * </ul>
49  * This class should not be instantiated until the onload event to ensure that
50  * the associated elements are available.
51  * The following would define a DragDrop obj that would interact with any
52  * other DragDrop obj in the "group1" group:
53  * <pre>
54  *  dd = new Roo.dd.DragDrop("div1", "group1");
55  * </pre>
56  * Since none of the event handlers have been implemented, nothing would
57  * actually happen if you were to run the code above.  Normally you would
58  * override this class or one of the default implementations, but you can
59  * also override the methods you want on an instance of the class...
60  * <pre>
61  *  dd.onDragDrop = function(e, id) {
62  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
63  *  }
64  * </pre>
65  * @constructor
66  * @param {String} id of the element that is linked to this instance
67  * @param {String} sGroup the group of related DragDrop objects
68  * @param {object} config an object containing configurable attributes
69  *                Valid properties for DragDrop:
70  *                    padding, isTarget, maintainOffset, primaryButtonOnly
71  */
72 Roo.dd.DragDrop = function(id, sGroup, config) {
73     if (id) {
74         this.init(id, sGroup, config);
75     }
76     
77 };
78
79 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
80
81     /**
82      * The id of the element associated with this object.  This is what we
83      * refer to as the "linked element" because the size and position of
84      * this element is used to determine when the drag and drop objects have
85      * interacted.
86      * @property id
87      * @type String
88      */
89     id: null,
90
91     /**
92      * Configuration attributes passed into the constructor
93      * @property config
94      * @type object
95      */
96     config: null,
97
98     /**
99      * The id of the element that will be dragged.  By default this is same
100      * as the linked element , but could be changed to another element. Ex:
101      * Roo.dd.DDProxy
102      * @property dragElId
103      * @type String
104      * @private
105      */
106     dragElId: null,
107
108     /**
109      * the id of the element that initiates the drag operation.  By default
110      * this is the linked element, but could be changed to be a child of this
111      * element.  This lets us do things like only starting the drag when the
112      * header element within the linked html element is clicked.
113      * @property handleElId
114      * @type String
115      * @private
116      */
117     handleElId: null,
118
119     /**
120      * An associative array of HTML tags that will be ignored if clicked.
121      * @property invalidHandleTypes
122      * @type {string: string}
123      */
124     invalidHandleTypes: null,
125
126     /**
127      * An associative array of ids for elements that will be ignored if clicked
128      * @property invalidHandleIds
129      * @type {string: string}
130      */
131     invalidHandleIds: null,
132
133     /**
134      * An indexted array of css class names for elements that will be ignored
135      * if clicked.
136      * @property invalidHandleClasses
137      * @type string[]
138      */
139     invalidHandleClasses: null,
140
141     /**
142      * The linked element's absolute X position at the time the drag was
143      * started
144      * @property startPageX
145      * @type int
146      * @private
147      */
148     startPageX: 0,
149
150     /**
151      * The linked element's absolute X position at the time the drag was
152      * started
153      * @property startPageY
154      * @type int
155      * @private
156      */
157     startPageY: 0,
158
159     /**
160      * The group defines a logical collection of DragDrop objects that are
161      * related.  Instances only get events when interacting with other
162      * DragDrop object in the same group.  This lets us define multiple
163      * groups using a single DragDrop subclass if we want.
164      * @property groups
165      * @type {string: string}
166      */
167     groups: null,
168
169     /**
170      * Individual drag/drop instances can be locked.  This will prevent
171      * onmousedown start drag.
172      * @property locked
173      * @type boolean
174      * @private
175      */
176     locked: false,
177
178     /**
179      * Lock this instance
180      * @method lock
181      */
182     lock: function() { this.locked = true; },
183
184     /**
185      * Unlock this instace
186      * @method unlock
187      */
188     unlock: function() { this.locked = false; },
189
190     /**
191      * By default, all insances can be a drop target.  This can be disabled by
192      * setting isTarget to false.
193      * @method isTarget
194      * @type boolean
195      */
196     isTarget: true,
197
198     /**
199      * The padding configured for this drag and drop object for calculating
200      * the drop zone intersection with this object.
201      * @method padding
202      * @type int[]
203      */
204     padding: null,
205
206     /**
207      * Cached reference to the linked element
208      * @property _domRef
209      * @private
210      */
211     _domRef: null,
212
213     /**
214      * Internal typeof flag
215      * @property __ygDragDrop
216      * @private
217      */
218     __ygDragDrop: true,
219
220     /**
221      * Set to true when horizontal contraints are applied
222      * @property constrainX
223      * @type boolean
224      * @private
225      */
226     constrainX: false,
227
228     /**
229      * Set to true when vertical contraints are applied
230      * @property constrainY
231      * @type boolean
232      * @private
233      */
234     constrainY: false,
235
236     /**
237      * The left constraint
238      * @property minX
239      * @type int
240      * @private
241      */
242     minX: 0,
243
244     /**
245      * The right constraint
246      * @property maxX
247      * @type int
248      * @private
249      */
250     maxX: 0,
251
252     /**
253      * The up constraint
254      * @property minY
255      * @type int
256      * @type int
257      * @private
258      */
259     minY: 0,
260
261     /**
262      * The down constraint
263      * @property maxY
264      * @type int
265      * @private
266      */
267     maxY: 0,
268
269     /**
270      * Maintain offsets when we resetconstraints.  Set to true when you want
271      * the position of the element relative to its parent to stay the same
272      * when the page changes
273      *
274      * @property maintainOffset
275      * @type boolean
276      */
277     maintainOffset: false,
278
279     /**
280      * Array of pixel locations the element will snap to if we specified a
281      * horizontal graduation/interval.  This array is generated automatically
282      * when you define a tick interval.
283      * @property xTicks
284      * @type int[]
285      */
286     xTicks: null,
287
288     /**
289      * Array of pixel locations the element will snap to if we specified a
290      * vertical graduation/interval.  This array is generated automatically
291      * when you define a tick interval.
292      * @property yTicks
293      * @type int[]
294      */
295     yTicks: null,
296
297     /**
298      * By default the drag and drop instance will only respond to the primary
299      * button click (left button for a right-handed mouse).  Set to true to
300      * allow drag and drop to start with any mouse click that is propogated
301      * by the browser
302      * @property primaryButtonOnly
303      * @type boolean
304      */
305     primaryButtonOnly: true,
306
307     /**
308      * The availabe property is false until the linked dom element is accessible.
309      * @property available
310      * @type boolean
311      */
312     available: false,
313
314     /**
315      * By default, drags can only be initiated if the mousedown occurs in the
316      * region the linked element is.  This is done in part to work around a
317      * bug in some browsers that mis-report the mousedown if the previous
318      * mouseup happened outside of the window.  This property is set to true
319      * if outer handles are defined.
320      *
321      * @property hasOuterHandles
322      * @type boolean
323      * @default false
324      */
325     hasOuterHandles: false,
326
327     /**
328      * Code that executes immediately before the startDrag event
329      * @method b4StartDrag
330      * @private
331      */
332     b4StartDrag: function(x, y) { },
333
334     /**
335      * Abstract method called after a drag/drop object is clicked
336      * and the drag or mousedown time thresholds have beeen met.
337      * @method startDrag
338      * @param {int} X click location
339      * @param {int} Y click location
340      */
341     startDrag: function(x, y) { /* override this */ },
342
343     /**
344      * Code that executes immediately before the onDrag event
345      * @method b4Drag
346      * @private
347      */
348     b4Drag: function(e) { },
349
350     /**
351      * Abstract method called during the onMouseMove event while dragging an
352      * object.
353      * @method onDrag
354      * @param {Event} e the mousemove event
355      */
356     onDrag: function(e) { /* override this */ },
357
358     /**
359      * Abstract method called when this element fist begins hovering over
360      * another DragDrop obj
361      * @method onDragEnter
362      * @param {Event} e the mousemove event
363      * @param {String|DragDrop[]} id In POINT mode, the element
364      * id this is hovering over.  In INTERSECT mode, an array of one or more
365      * dragdrop items being hovered over.
366      */
367     onDragEnter: function(e, id) { /* override this */ },
368
369     /**
370      * Code that executes immediately before the onDragOver event
371      * @method b4DragOver
372      * @private
373      */
374     b4DragOver: function(e) { },
375
376     /**
377      * Abstract method called when this element is hovering over another
378      * DragDrop obj
379      * @method onDragOver
380      * @param {Event} e the mousemove event
381      * @param {String|DragDrop[]} id In POINT mode, the element
382      * id this is hovering over.  In INTERSECT mode, an array of dd items
383      * being hovered over.
384      */
385     onDragOver: function(e, id) { /* override this */ },
386
387     /**
388      * Code that executes immediately before the onDragOut event
389      * @method b4DragOut
390      * @private
391      */
392     b4DragOut: function(e) { },
393
394     /**
395      * Abstract method called when we are no longer hovering over an element
396      * @method onDragOut
397      * @param {Event} e the mousemove event
398      * @param {String|DragDrop[]} id In POINT mode, the element
399      * id this was hovering over.  In INTERSECT mode, an array of dd items
400      * that the mouse is no longer over.
401      */
402     onDragOut: function(e, id) { /* override this */ },
403
404     /**
405      * Code that executes immediately before the onDragDrop event
406      * @method b4DragDrop
407      * @private
408      */
409     b4DragDrop: function(e) { },
410
411     /**
412      * Abstract method called when this item is dropped on another DragDrop
413      * obj
414      * @method onDragDrop
415      * @param {Event} e the mouseup event
416      * @param {String|DragDrop[]} id In POINT mode, the element
417      * id this was dropped on.  In INTERSECT mode, an array of dd items this
418      * was dropped on.
419      */
420     onDragDrop: function(e, id) { /* override this */ },
421
422     /**
423      * Abstract method called when this item is dropped on an area with no
424      * drop target
425      * @method onInvalidDrop
426      * @param {Event} e the mouseup event
427      */
428     onInvalidDrop: function(e) { /* override this */ },
429
430     /**
431      * Code that executes immediately before the endDrag event
432      * @method b4EndDrag
433      * @private
434      */
435     b4EndDrag: function(e) { },
436
437     /**
438      * Fired when we are done dragging the object
439      * @method endDrag
440      * @param {Event} e the mouseup event
441      */
442     endDrag: function(e) { /* override this */ },
443
444     /**
445      * Code executed immediately before the onMouseDown event
446      * @method b4MouseDown
447      * @param {Event} e the mousedown event
448      * @private
449      */
450     b4MouseDown: function(e) {  },
451
452     /**
453      * Event handler that fires when a drag/drop obj gets a mousedown
454      * @method onMouseDown
455      * @param {Event} e the mousedown event
456      */
457     onMouseDown: function(e) { /* override this */ },
458
459     /**
460      * Event handler that fires when a drag/drop obj gets a mouseup
461      * @method onMouseUp
462      * @param {Event} e the mouseup event
463      */
464     onMouseUp: function(e) { /* override this */ },
465
466     /**
467      * Override the onAvailable method to do what is needed after the initial
468      * position was determined.
469      * @method onAvailable
470      */
471     onAvailable: function () {
472     },
473
474     /*
475      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
476      * @type Object
477      */
478     defaultPadding : {left:0, right:0, top:0, bottom:0},
479
480     /*
481      * Initializes the drag drop object's constraints to restrict movement to a certain element.
482  *
483  * Usage:
484  <pre><code>
485  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
486                 { dragElId: "existingProxyDiv" });
487  dd.startDrag = function(){
488      this.constrainTo("parent-id");
489  };
490  </code></pre>
491  * Or you can initalize it using the {@link Roo.Element} object:
492  <pre><code>
493  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
494      startDrag : function(){
495          this.constrainTo("parent-id");
496      }
497  });
498  </code></pre>
499      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
500      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
501      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
502      * an object containing the sides to pad. For example: {right:10, bottom:10}
503      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
504      */
505     constrainTo : function(constrainTo, pad, inContent){
506         if(typeof pad == "number"){
507             pad = {left: pad, right:pad, top:pad, bottom:pad};
508         }
509         pad = pad || this.defaultPadding;
510         var b = Roo.get(this.getEl()).getBox();
511         var ce = Roo.get(constrainTo);
512         var s = ce.getScroll();
513         var c, cd = ce.dom;
514         if(cd == document.body){
515             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
516         }else{
517             xy = ce.getXY();
518             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
519         }
520
521
522         var topSpace = b.y - c.y;
523         var leftSpace = b.x - c.x;
524
525         this.resetConstraints();
526         this.setXConstraint(leftSpace - (pad.left||0), // left
527                 c.width - leftSpace - b.width - (pad.right||0) //right
528         );
529         this.setYConstraint(topSpace - (pad.top||0), //top
530                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
531         );
532     },
533
534     /**
535      * Returns a reference to the linked element
536      * @method getEl
537      * @return {HTMLElement} the html element
538      */
539     getEl: function() {
540         if (!this._domRef) {
541             this._domRef = Roo.getDom(this.id);
542         }
543
544         return this._domRef;
545     },
546
547     /**
548      * Returns a reference to the actual element to drag.  By default this is
549      * the same as the html element, but it can be assigned to another
550      * element. An example of this can be found in Roo.dd.DDProxy
551      * @method getDragEl
552      * @return {HTMLElement} the html element
553      */
554     getDragEl: function() {
555         return Roo.getDom(this.dragElId);
556     },
557
558     /**
559      * Sets up the DragDrop object.  Must be called in the constructor of any
560      * Roo.dd.DragDrop subclass
561      * @method init
562      * @param id the id of the linked element
563      * @param {String} sGroup the group of related items
564      * @param {object} config configuration attributes
565      */
566     init: function(id, sGroup, config) {
567         this.initTarget(id, sGroup, config);
568         if (!Roo.isTouch) {
569             Event.on(this.id, "mousedown", this.handleMouseDown, this);
570         }
571         Event.on(this.id, "touchstart", this.handleMouseDown, this);
572         // Event.on(this.id, "selectstart", Event.preventDefault);
573     },
574
575     /**
576      * Initializes Targeting functionality only... the object does not
577      * get a mousedown handler.
578      * @method initTarget
579      * @param id the id of the linked element
580      * @param {String} sGroup the group of related items
581      * @param {object} config configuration attributes
582      */
583     initTarget: function(id, sGroup, config) {
584
585         // configuration attributes
586         this.config = config || {};
587
588         // create a local reference to the drag and drop manager
589         this.DDM = Roo.dd.DDM;
590         // initialize the groups array
591         this.groups = {};
592
593         // assume that we have an element reference instead of an id if the
594         // parameter is not a string
595         if (typeof id !== "string") {
596             id = Roo.id(id);
597         }
598
599         // set the id
600         this.id = id;
601
602         // add to an interaction group
603         this.addToGroup((sGroup) ? sGroup : "default");
604
605         // We don't want to register this as the handle with the manager
606         // so we just set the id rather than calling the setter.
607         this.handleElId = id;
608
609         // the linked element is the element that gets dragged by default
610         this.setDragElId(id);
611
612         // by default, clicked anchors will not start drag operations.
613         this.invalidHandleTypes = { A: "A" };
614         this.invalidHandleIds = {};
615         this.invalidHandleClasses = [];
616
617         this.applyConfig();
618
619         this.handleOnAvailable();
620     },
621
622     /**
623      * Applies the configuration parameters that were passed into the constructor.
624      * This is supposed to happen at each level through the inheritance chain.  So
625      * a DDProxy implentation will execute apply config on DDProxy, DD, and
626      * DragDrop in order to get all of the parameters that are available in
627      * each object.
628      * @method applyConfig
629      */
630     applyConfig: function() {
631
632         // configurable properties:
633         //    padding, isTarget, maintainOffset, primaryButtonOnly
634         this.padding           = this.config.padding || [0, 0, 0, 0];
635         this.isTarget          = (this.config.isTarget !== false);
636         this.maintainOffset    = (this.config.maintainOffset);
637         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
638
639     },
640
641     /**
642      * Executed when the linked element is available
643      * @method handleOnAvailable
644      * @private
645      */
646     handleOnAvailable: function() {
647         this.available = true;
648         this.resetConstraints();
649         this.onAvailable();
650     },
651
652      /**
653      * Configures the padding for the target zone in px.  Effectively expands
654      * (or reduces) the virtual object size for targeting calculations.
655      * Supports css-style shorthand; if only one parameter is passed, all sides
656      * will have that padding, and if only two are passed, the top and bottom
657      * will have the first param, the left and right the second.
658      * @method setPadding
659      * @param {int} iTop    Top pad
660      * @param {int} iRight  Right pad
661      * @param {int} iBot    Bot pad
662      * @param {int} iLeft   Left pad
663      */
664     setPadding: function(iTop, iRight, iBot, iLeft) {
665         // this.padding = [iLeft, iRight, iTop, iBot];
666         if (!iRight && 0 !== iRight) {
667             this.padding = [iTop, iTop, iTop, iTop];
668         } else if (!iBot && 0 !== iBot) {
669             this.padding = [iTop, iRight, iTop, iRight];
670         } else {
671             this.padding = [iTop, iRight, iBot, iLeft];
672         }
673     },
674
675     /**
676      * Stores the initial placement of the linked element.
677      * @method setInitialPosition
678      * @param {int} diffX   the X offset, default 0
679      * @param {int} diffY   the Y offset, default 0
680      */
681     setInitPosition: function(diffX, diffY) {
682         var el = this.getEl();
683
684         if (!this.DDM.verifyEl(el)) {
685             return;
686         }
687
688         var dx = diffX || 0;
689         var dy = diffY || 0;
690
691         var p = Dom.getXY( el );
692
693         this.initPageX = p[0] - dx;
694         this.initPageY = p[1] - dy;
695
696         this.lastPageX = p[0];
697         this.lastPageY = p[1];
698
699
700         this.setStartPosition(p);
701     },
702
703     /**
704      * Sets the start position of the element.  This is set when the obj
705      * is initialized, the reset when a drag is started.
706      * @method setStartPosition
707      * @param pos current position (from previous lookup)
708      * @private
709      */
710     setStartPosition: function(pos) {
711         var p = pos || Dom.getXY( this.getEl() );
712         this.deltaSetXY = null;
713
714         this.startPageX = p[0];
715         this.startPageY = p[1];
716     },
717
718     /**
719      * Add this instance to a group of related drag/drop objects.  All
720      * instances belong to at least one group, and can belong to as many
721      * groups as needed.
722      * @method addToGroup
723      * @param sGroup {string} the name of the group
724      */
725     addToGroup: function(sGroup) {
726         this.groups[sGroup] = true;
727         this.DDM.regDragDrop(this, sGroup);
728     },
729
730     /**
731      * Remove's this instance from the supplied interaction group
732      * @method removeFromGroup
733      * @param {string}  sGroup  The group to drop
734      */
735     removeFromGroup: function(sGroup) {
736         if (this.groups[sGroup]) {
737             delete this.groups[sGroup];
738         }
739
740         this.DDM.removeDDFromGroup(this, sGroup);
741     },
742
743     /**
744      * Allows you to specify that an element other than the linked element
745      * will be moved with the cursor during a drag
746      * @method setDragElId
747      * @param id {string} the id of the element that will be used to initiate the drag
748      */
749     setDragElId: function(id) {
750         this.dragElId = id;
751     },
752
753     /**
754      * Allows you to specify a child of the linked element that should be
755      * used to initiate the drag operation.  An example of this would be if
756      * you have a content div with text and links.  Clicking anywhere in the
757      * content area would normally start the drag operation.  Use this method
758      * to specify that an element inside of the content div is the element
759      * that starts the drag operation.
760      * @method setHandleElId
761      * @param id {string} the id of the element that will be used to
762      * initiate the drag.
763      */
764     setHandleElId: function(id) {
765         if (typeof id !== "string") {
766             id = Roo.id(id);
767         }
768         this.handleElId = id;
769         this.DDM.regHandle(this.id, id);
770     },
771
772     /**
773      * Allows you to set an element outside of the linked element as a drag
774      * handle
775      * @method setOuterHandleElId
776      * @param id the id of the element that will be used to initiate the drag
777      */
778     setOuterHandleElId: function(id) {
779         if (typeof id !== "string") {
780             id = Roo.id(id);
781         }
782         Event.on(id, "mousedown",
783                 this.handleMouseDown, this);
784         this.setHandleElId(id);
785
786         this.hasOuterHandles = true;
787     },
788
789     /**
790      * Remove all drag and drop hooks for this element
791      * @method unreg
792      */
793     unreg: function() {
794         Event.un(this.id, "mousedown",
795                 this.handleMouseDown);
796         Event.un(this.id, "touchstart",
797                 this.handleMouseDown);
798         this._domRef = null;
799         this.DDM._remove(this);
800     },
801
802     destroy : function(){
803         this.unreg();
804     },
805
806     /**
807      * Returns true if this instance is locked, or the drag drop mgr is locked
808      * (meaning that all drag/drop is disabled on the page.)
809      * @method isLocked
810      * @return {boolean} true if this obj or all drag/drop is locked, else
811      * false
812      */
813     isLocked: function() {
814         return (this.DDM.isLocked() || this.locked);
815     },
816
817     /**
818      * Fired when this object is clicked
819      * @method handleMouseDown
820      * @param {Event} e
821      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
822      * @private
823      */
824     handleMouseDown: function(e, oDD){
825      
826         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
827             //Roo.log('not touch/ button !=0');
828             return;
829         }
830         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
831             return; // double touch..
832         }
833         
834
835         if (this.isLocked()) {
836             //Roo.log('locked');
837             return;
838         }
839
840         this.DDM.refreshCache(this.groups);
841 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
842         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
843         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
844             //Roo.log('no outer handes or not over target');
845                 // do nothing.
846         } else {
847 //            Roo.log('check validator');
848             if (this.clickValidator(e)) {
849 //                Roo.log('validate success');
850                 // set the initial element position
851                 this.setStartPosition();
852
853
854                 this.b4MouseDown(e);
855                 this.onMouseDown(e);
856
857                 this.DDM.handleMouseDown(e, this);
858
859                 this.DDM.stopEvent(e);
860             } else {
861
862
863             }
864         }
865     },
866
867     clickValidator: function(e) {
868         var target = e.getTarget();
869         return ( this.isValidHandleChild(target) &&
870                     (this.id == this.handleElId ||
871                         this.DDM.handleWasClicked(target, this.id)) );
872     },
873
874     /**
875      * Allows you to specify a tag name that should not start a drag operation
876      * when clicked.  This is designed to facilitate embedding links within a
877      * drag handle that do something other than start the drag.
878      * @method addInvalidHandleType
879      * @param {string} tagName the type of element to exclude
880      */
881     addInvalidHandleType: function(tagName) {
882         var type = tagName.toUpperCase();
883         this.invalidHandleTypes[type] = type;
884     },
885
886     /**
887      * Lets you to specify an element id for a child of a drag handle
888      * that should not initiate a drag
889      * @method addInvalidHandleId
890      * @param {string} id the element id of the element you wish to ignore
891      */
892     addInvalidHandleId: function(id) {
893         if (typeof id !== "string") {
894             id = Roo.id(id);
895         }
896         this.invalidHandleIds[id] = id;
897     },
898
899     /**
900      * Lets you specify a css class of elements that will not initiate a drag
901      * @method addInvalidHandleClass
902      * @param {string} cssClass the class of the elements you wish to ignore
903      */
904     addInvalidHandleClass: function(cssClass) {
905         this.invalidHandleClasses.push(cssClass);
906     },
907
908     /**
909      * Unsets an excluded tag name set by addInvalidHandleType
910      * @method removeInvalidHandleType
911      * @param {string} tagName the type of element to unexclude
912      */
913     removeInvalidHandleType: function(tagName) {
914         var type = tagName.toUpperCase();
915         // this.invalidHandleTypes[type] = null;
916         delete this.invalidHandleTypes[type];
917     },
918
919     /**
920      * Unsets an invalid handle id
921      * @method removeInvalidHandleId
922      * @param {string} id the id of the element to re-enable
923      */
924     removeInvalidHandleId: function(id) {
925         if (typeof id !== "string") {
926             id = Roo.id(id);
927         }
928         delete this.invalidHandleIds[id];
929     },
930
931     /**
932      * Unsets an invalid css class
933      * @method removeInvalidHandleClass
934      * @param {string} cssClass the class of the element(s) you wish to
935      * re-enable
936      */
937     removeInvalidHandleClass: function(cssClass) {
938         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
939             if (this.invalidHandleClasses[i] == cssClass) {
940                 delete this.invalidHandleClasses[i];
941             }
942         }
943     },
944
945     /**
946      * Checks the tag exclusion list to see if this click should be ignored
947      * @method isValidHandleChild
948      * @param {HTMLElement} node the HTMLElement to evaluate
949      * @return {boolean} true if this is a valid tag type, false if not
950      */
951     isValidHandleChild: function(node) {
952
953         var valid = true;
954         // var n = (node.nodeName == "#text") ? node.parentNode : node;
955         var nodeName;
956         try {
957             nodeName = node.nodeName.toUpperCase();
958         } catch(e) {
959             nodeName = node.nodeName;
960         }
961         valid = valid && !this.invalidHandleTypes[nodeName];
962         valid = valid && !this.invalidHandleIds[node.id];
963
964         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
965             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
966         }
967
968
969         return valid;
970
971     },
972
973     /**
974      * Create the array of horizontal tick marks if an interval was specified
975      * in setXConstraint().
976      * @method setXTicks
977      * @private
978      */
979     setXTicks: function(iStartX, iTickSize) {
980         this.xTicks = [];
981         this.xTickSize = iTickSize;
982
983         var tickMap = {};
984
985         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
986             if (!tickMap[i]) {
987                 this.xTicks[this.xTicks.length] = i;
988                 tickMap[i] = true;
989             }
990         }
991
992         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
993             if (!tickMap[i]) {
994                 this.xTicks[this.xTicks.length] = i;
995                 tickMap[i] = true;
996             }
997         }
998
999         this.xTicks.sort(this.DDM.numericSort) ;
1000     },
1001
1002     /**
1003      * Create the array of vertical tick marks if an interval was specified in
1004      * setYConstraint().
1005      * @method setYTicks
1006      * @private
1007      */
1008     setYTicks: function(iStartY, iTickSize) {
1009         this.yTicks = [];
1010         this.yTickSize = iTickSize;
1011
1012         var tickMap = {};
1013
1014         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1015             if (!tickMap[i]) {
1016                 this.yTicks[this.yTicks.length] = i;
1017                 tickMap[i] = true;
1018             }
1019         }
1020
1021         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1022             if (!tickMap[i]) {
1023                 this.yTicks[this.yTicks.length] = i;
1024                 tickMap[i] = true;
1025             }
1026         }
1027
1028         this.yTicks.sort(this.DDM.numericSort) ;
1029     },
1030
1031     /**
1032      * By default, the element can be dragged any place on the screen.  Use
1033      * this method to limit the horizontal travel of the element.  Pass in
1034      * 0,0 for the parameters if you want to lock the drag to the y axis.
1035      * @method setXConstraint
1036      * @param {int} iLeft the number of pixels the element can move to the left
1037      * @param {int} iRight the number of pixels the element can move to the
1038      * right
1039      * @param {int} iTickSize optional parameter for specifying that the
1040      * element
1041      * should move iTickSize pixels at a time.
1042      */
1043     setXConstraint: function(iLeft, iRight, iTickSize) {
1044         this.leftConstraint = iLeft;
1045         this.rightConstraint = iRight;
1046
1047         this.minX = this.initPageX - iLeft;
1048         this.maxX = this.initPageX + iRight;
1049         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1050
1051         this.constrainX = true;
1052     },
1053
1054     /**
1055      * Clears any constraints applied to this instance.  Also clears ticks
1056      * since they can't exist independent of a constraint at this time.
1057      * @method clearConstraints
1058      */
1059     clearConstraints: function() {
1060         this.constrainX = false;
1061         this.constrainY = false;
1062         this.clearTicks();
1063     },
1064
1065     /**
1066      * Clears any tick interval defined for this instance
1067      * @method clearTicks
1068      */
1069     clearTicks: function() {
1070         this.xTicks = null;
1071         this.yTicks = null;
1072         this.xTickSize = 0;
1073         this.yTickSize = 0;
1074     },
1075
1076     /**
1077      * By default, the element can be dragged any place on the screen.  Set
1078      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1079      * parameters if you want to lock the drag to the x axis.
1080      * @method setYConstraint
1081      * @param {int} iUp the number of pixels the element can move up
1082      * @param {int} iDown the number of pixels the element can move down
1083      * @param {int} iTickSize optional parameter for specifying that the
1084      * element should move iTickSize pixels at a time.
1085      */
1086     setYConstraint: function(iUp, iDown, iTickSize) {
1087         this.topConstraint = iUp;
1088         this.bottomConstraint = iDown;
1089
1090         this.minY = this.initPageY - iUp;
1091         this.maxY = this.initPageY + iDown;
1092         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1093
1094         this.constrainY = true;
1095
1096     },
1097
1098     /**
1099      * resetConstraints must be called if you manually reposition a dd element.
1100      * @method resetConstraints
1101      * @param {boolean} maintainOffset
1102      */
1103     resetConstraints: function() {
1104
1105
1106         // Maintain offsets if necessary
1107         if (this.initPageX || this.initPageX === 0) {
1108             // figure out how much this thing has moved
1109             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1110             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1111
1112             this.setInitPosition(dx, dy);
1113
1114         // This is the first time we have detected the element's position
1115         } else {
1116             this.setInitPosition();
1117         }
1118
1119         if (this.constrainX) {
1120             this.setXConstraint( this.leftConstraint,
1121                                  this.rightConstraint,
1122                                  this.xTickSize        );
1123         }
1124
1125         if (this.constrainY) {
1126             this.setYConstraint( this.topConstraint,
1127                                  this.bottomConstraint,
1128                                  this.yTickSize         );
1129         }
1130     },
1131
1132     /**
1133      * Normally the drag element is moved pixel by pixel, but we can specify
1134      * that it move a number of pixels at a time.  This method resolves the
1135      * location when we have it set up like this.
1136      * @method getTick
1137      * @param {int} val where we want to place the object
1138      * @param {int[]} tickArray sorted array of valid points
1139      * @return {int} the closest tick
1140      * @private
1141      */
1142     getTick: function(val, tickArray) {
1143
1144         if (!tickArray) {
1145             // If tick interval is not defined, it is effectively 1 pixel,
1146             // so we return the value passed to us.
1147             return val;
1148         } else if (tickArray[0] >= val) {
1149             // The value is lower than the first tick, so we return the first
1150             // tick.
1151             return tickArray[0];
1152         } else {
1153             for (var i=0, len=tickArray.length; i<len; ++i) {
1154                 var next = i + 1;
1155                 if (tickArray[next] && tickArray[next] >= val) {
1156                     var diff1 = val - tickArray[i];
1157                     var diff2 = tickArray[next] - val;
1158                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1159                 }
1160             }
1161
1162             // The value is larger than the last tick, so we return the last
1163             // tick.
1164             return tickArray[tickArray.length - 1];
1165         }
1166     },
1167
1168     /**
1169      * toString method
1170      * @method toString
1171      * @return {string} string representation of the dd obj
1172      */
1173     toString: function() {
1174         return ("DragDrop " + this.id);
1175     }
1176
1177 });
1178
1179 })();
1180 /*
1181  * Based on:
1182  * Ext JS Library 1.1.1
1183  * Copyright(c) 2006-2007, Ext JS, LLC.
1184  *
1185  * Originally Released Under LGPL - original licence link has changed is not relivant.
1186  *
1187  * Fork - LGPL
1188  * <script type="text/javascript">
1189  */
1190
1191
1192 /**
1193  * The drag and drop utility provides a framework for building drag and drop
1194  * applications.  In addition to enabling drag and drop for specific elements,
1195  * the drag and drop elements are tracked by the manager class, and the
1196  * interactions between the various elements are tracked during the drag and
1197  * the implementing code is notified about these important moments.
1198  */
1199
1200 // Only load the library once.  Rewriting the manager class would orphan
1201 // existing drag and drop instances.
1202 if (!Roo.dd.DragDropMgr) {
1203
1204 /**
1205  * @class Roo.dd.DragDropMgr
1206  * DragDropMgr is a singleton that tracks the element interaction for
1207  * all DragDrop items in the window.  Generally, you will not call
1208  * this class directly, but it does have helper methods that could
1209  * be useful in your DragDrop implementations.
1210  * @singleton
1211  */
1212 Roo.dd.DragDropMgr = function() {
1213
1214     var Event = Roo.EventManager;
1215
1216     return {
1217
1218         /**
1219          * Two dimensional Array of registered DragDrop objects.  The first
1220          * dimension is the DragDrop item group, the second the DragDrop
1221          * object.
1222          * @property ids
1223          * @type {string: string}
1224          * @private
1225          * @static
1226          */
1227         ids: {},
1228
1229         /**
1230          * Array of element ids defined as drag handles.  Used to determine
1231          * if the element that generated the mousedown event is actually the
1232          * handle and not the html element itself.
1233          * @property handleIds
1234          * @type {string: string}
1235          * @private
1236          * @static
1237          */
1238         handleIds: {},
1239
1240         /**
1241          * the DragDrop object that is currently being dragged
1242          * @property dragCurrent
1243          * @type DragDrop
1244          * @private
1245          * @static
1246          **/
1247         dragCurrent: null,
1248
1249         /**
1250          * the DragDrop object(s) that are being hovered over
1251          * @property dragOvers
1252          * @type Array
1253          * @private
1254          * @static
1255          */
1256         dragOvers: {},
1257
1258         /**
1259          * the X distance between the cursor and the object being dragged
1260          * @property deltaX
1261          * @type int
1262          * @private
1263          * @static
1264          */
1265         deltaX: 0,
1266
1267         /**
1268          * the Y distance between the cursor and the object being dragged
1269          * @property deltaY
1270          * @type int
1271          * @private
1272          * @static
1273          */
1274         deltaY: 0,
1275
1276         /**
1277          * Flag to determine if we should prevent the default behavior of the
1278          * events we define. By default this is true, but this can be set to
1279          * false if you need the default behavior (not recommended)
1280          * @property preventDefault
1281          * @type boolean
1282          * @static
1283          */
1284         preventDefault: true,
1285
1286         /**
1287          * Flag to determine if we should stop the propagation of the events
1288          * we generate. This is true by default but you may want to set it to
1289          * false if the html element contains other features that require the
1290          * mouse click.
1291          * @property stopPropagation
1292          * @type boolean
1293          * @static
1294          */
1295         stopPropagation: true,
1296
1297         /**
1298          * Internal flag that is set to true when drag and drop has been
1299          * intialized
1300          * @property initialized
1301          * @private
1302          * @static
1303          */
1304         initalized: false,
1305
1306         /**
1307          * All drag and drop can be disabled.
1308          * @property locked
1309          * @private
1310          * @static
1311          */
1312         locked: false,
1313
1314         /**
1315          * Called the first time an element is registered.
1316          * @method init
1317          * @private
1318          * @static
1319          */
1320         init: function() {
1321             this.initialized = true;
1322         },
1323
1324         /**
1325          * In point mode, drag and drop interaction is defined by the
1326          * location of the cursor during the drag/drop
1327          * @property POINT
1328          * @type int
1329          * @static
1330          */
1331         POINT: 0,
1332
1333         /**
1334          * In intersect mode, drag and drop interactio nis defined by the
1335          * overlap of two or more drag and drop objects.
1336          * @property INTERSECT
1337          * @type int
1338          * @static
1339          */
1340         INTERSECT: 1,
1341
1342         /**
1343          * The current drag and drop mode.  Default: POINT
1344          * @property mode
1345          * @type int
1346          * @static
1347          */
1348         mode: 0,
1349
1350         /**
1351          * Runs method on all drag and drop objects
1352          * @method _execOnAll
1353          * @private
1354          * @static
1355          */
1356         _execOnAll: function(sMethod, args) {
1357             for (var i in this.ids) {
1358                 for (var j in this.ids[i]) {
1359                     var oDD = this.ids[i][j];
1360                     if (! this.isTypeOfDD(oDD)) {
1361                         continue;
1362                     }
1363                     oDD[sMethod].apply(oDD, args);
1364                 }
1365             }
1366         },
1367
1368         /**
1369          * Drag and drop initialization.  Sets up the global event handlers
1370          * @method _onLoad
1371          * @private
1372          * @static
1373          */
1374         _onLoad: function() {
1375
1376             this.init();
1377
1378             if (!Roo.isTouch) {
1379                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1380                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
1381             }
1382             Event.on(document, "touchend",   this.handleMouseUp, this, true);
1383             Event.on(document, "touchmove", this.handleMouseMove, this, true);
1384             
1385             Event.on(window,   "unload",    this._onUnload, this, true);
1386             Event.on(window,   "resize",    this._onResize, this, true);
1387             // Event.on(window,   "mouseout",    this._test);
1388
1389         },
1390
1391         /**
1392          * Reset constraints on all drag and drop objs
1393          * @method _onResize
1394          * @private
1395          * @static
1396          */
1397         _onResize: function(e) {
1398             this._execOnAll("resetConstraints", []);
1399         },
1400
1401         /**
1402          * Lock all drag and drop functionality
1403          * @method lock
1404          * @static
1405          */
1406         lock: function() { this.locked = true; },
1407
1408         /**
1409          * Unlock all drag and drop functionality
1410          * @method unlock
1411          * @static
1412          */
1413         unlock: function() { this.locked = false; },
1414
1415         /**
1416          * Is drag and drop locked?
1417          * @method isLocked
1418          * @return {boolean} True if drag and drop is locked, false otherwise.
1419          * @static
1420          */
1421         isLocked: function() { return this.locked; },
1422
1423         /**
1424          * Location cache that is set for all drag drop objects when a drag is
1425          * initiated, cleared when the drag is finished.
1426          * @property locationCache
1427          * @private
1428          * @static
1429          */
1430         locationCache: {},
1431
1432         /**
1433          * Set useCache to false if you want to force object the lookup of each
1434          * drag and drop linked element constantly during a drag.
1435          * @property useCache
1436          * @type boolean
1437          * @static
1438          */
1439         useCache: true,
1440
1441         /**
1442          * The number of pixels that the mouse needs to move after the
1443          * mousedown before the drag is initiated.  Default=3;
1444          * @property clickPixelThresh
1445          * @type int
1446          * @static
1447          */
1448         clickPixelThresh: 3,
1449
1450         /**
1451          * The number of milliseconds after the mousedown event to initiate the
1452          * drag if we don't get a mouseup event. Default=1000
1453          * @property clickTimeThresh
1454          * @type int
1455          * @static
1456          */
1457         clickTimeThresh: 350,
1458
1459         /**
1460          * Flag that indicates that either the drag pixel threshold or the
1461          * mousdown time threshold has been met
1462          * @property dragThreshMet
1463          * @type boolean
1464          * @private
1465          * @static
1466          */
1467         dragThreshMet: false,
1468
1469         /**
1470          * Timeout used for the click time threshold
1471          * @property clickTimeout
1472          * @type Object
1473          * @private
1474          * @static
1475          */
1476         clickTimeout: null,
1477
1478         /**
1479          * The X position of the mousedown event stored for later use when a
1480          * drag threshold is met.
1481          * @property startX
1482          * @type int
1483          * @private
1484          * @static
1485          */
1486         startX: 0,
1487
1488         /**
1489          * The Y position of the mousedown event stored for later use when a
1490          * drag threshold is met.
1491          * @property startY
1492          * @type int
1493          * @private
1494          * @static
1495          */
1496         startY: 0,
1497
1498         /**
1499          * Each DragDrop instance must be registered with the DragDropMgr.
1500          * This is executed in DragDrop.init()
1501          * @method regDragDrop
1502          * @param {DragDrop} oDD the DragDrop object to register
1503          * @param {String} sGroup the name of the group this element belongs to
1504          * @static
1505          */
1506         regDragDrop: function(oDD, sGroup) {
1507             if (!this.initialized) { this.init(); }
1508
1509             if (!this.ids[sGroup]) {
1510                 this.ids[sGroup] = {};
1511             }
1512             this.ids[sGroup][oDD.id] = oDD;
1513         },
1514
1515         /**
1516          * Removes the supplied dd instance from the supplied group. Executed
1517          * by DragDrop.removeFromGroup, so don't call this function directly.
1518          * @method removeDDFromGroup
1519          * @private
1520          * @static
1521          */
1522         removeDDFromGroup: function(oDD, sGroup) {
1523             if (!this.ids[sGroup]) {
1524                 this.ids[sGroup] = {};
1525             }
1526
1527             var obj = this.ids[sGroup];
1528             if (obj && obj[oDD.id]) {
1529                 delete obj[oDD.id];
1530             }
1531         },
1532
1533         /**
1534          * Unregisters a drag and drop item.  This is executed in
1535          * DragDrop.unreg, use that method instead of calling this directly.
1536          * @method _remove
1537          * @private
1538          * @static
1539          */
1540         _remove: function(oDD) {
1541             for (var g in oDD.groups) {
1542                 if (g && this.ids[g][oDD.id]) {
1543                     delete this.ids[g][oDD.id];
1544                 }
1545             }
1546             delete this.handleIds[oDD.id];
1547         },
1548
1549         /**
1550          * Each DragDrop handle element must be registered.  This is done
1551          * automatically when executing DragDrop.setHandleElId()
1552          * @method regHandle
1553          * @param {String} sDDId the DragDrop id this element is a handle for
1554          * @param {String} sHandleId the id of the element that is the drag
1555          * handle
1556          * @static
1557          */
1558         regHandle: function(sDDId, sHandleId) {
1559             if (!this.handleIds[sDDId]) {
1560                 this.handleIds[sDDId] = {};
1561             }
1562             this.handleIds[sDDId][sHandleId] = sHandleId;
1563         },
1564
1565         /**
1566          * Utility function to determine if a given element has been
1567          * registered as a drag drop item.
1568          * @method isDragDrop
1569          * @param {String} id the element id to check
1570          * @return {boolean} true if this element is a DragDrop item,
1571          * false otherwise
1572          * @static
1573          */
1574         isDragDrop: function(id) {
1575             return ( this.getDDById(id) ) ? true : false;
1576         },
1577
1578         /**
1579          * Returns the drag and drop instances that are in all groups the
1580          * passed in instance belongs to.
1581          * @method getRelated
1582          * @param {DragDrop} p_oDD the obj to get related data for
1583          * @param {boolean} bTargetsOnly if true, only return targetable objs
1584          * @return {DragDrop[]} the related instances
1585          * @static
1586          */
1587         getRelated: function(p_oDD, bTargetsOnly) {
1588             var oDDs = [];
1589             for (var i in p_oDD.groups) {
1590                 for (j in this.ids[i]) {
1591                     var dd = this.ids[i][j];
1592                     if (! this.isTypeOfDD(dd)) {
1593                         continue;
1594                     }
1595                     if (!bTargetsOnly || dd.isTarget) {
1596                         oDDs[oDDs.length] = dd;
1597                     }
1598                 }
1599             }
1600
1601             return oDDs;
1602         },
1603
1604         /**
1605          * Returns true if the specified dd target is a legal target for
1606          * the specifice drag obj
1607          * @method isLegalTarget
1608          * @param {DragDrop} the drag obj
1609          * @param {DragDrop} the target
1610          * @return {boolean} true if the target is a legal target for the
1611          * dd obj
1612          * @static
1613          */
1614         isLegalTarget: function (oDD, oTargetDD) {
1615             var targets = this.getRelated(oDD, true);
1616             for (var i=0, len=targets.length;i<len;++i) {
1617                 if (targets[i].id == oTargetDD.id) {
1618                     return true;
1619                 }
1620             }
1621
1622             return false;
1623         },
1624
1625         /**
1626          * My goal is to be able to transparently determine if an object is
1627          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1628          * returns "object", oDD.constructor.toString() always returns
1629          * "DragDrop" and not the name of the subclass.  So for now it just
1630          * evaluates a well-known variable in DragDrop.
1631          * @method isTypeOfDD
1632          * @param {Object} the object to evaluate
1633          * @return {boolean} true if typeof oDD = DragDrop
1634          * @static
1635          */
1636         isTypeOfDD: function (oDD) {
1637             return (oDD && oDD.__ygDragDrop);
1638         },
1639
1640         /**
1641          * Utility function to determine if a given element has been
1642          * registered as a drag drop handle for the given Drag Drop object.
1643          * @method isHandle
1644          * @param {String} id the element id to check
1645          * @return {boolean} true if this element is a DragDrop handle, false
1646          * otherwise
1647          * @static
1648          */
1649         isHandle: function(sDDId, sHandleId) {
1650             return ( this.handleIds[sDDId] &&
1651                             this.handleIds[sDDId][sHandleId] );
1652         },
1653
1654         /**
1655          * Returns the DragDrop instance for a given id
1656          * @method getDDById
1657          * @param {String} id the id of the DragDrop object
1658          * @return {DragDrop} the drag drop object, null if it is not found
1659          * @static
1660          */
1661         getDDById: function(id) {
1662             for (var i in this.ids) {
1663                 if (this.ids[i][id]) {
1664                     return this.ids[i][id];
1665                 }
1666             }
1667             return null;
1668         },
1669
1670         /**
1671          * Fired after a registered DragDrop object gets the mousedown event.
1672          * Sets up the events required to track the object being dragged
1673          * @method handleMouseDown
1674          * @param {Event} e the event
1675          * @param oDD the DragDrop object being dragged
1676          * @private
1677          * @static
1678          */
1679         handleMouseDown: function(e, oDD) {
1680             if(Roo.QuickTips){
1681                 Roo.QuickTips.disable();
1682             }
1683             this.currentTarget = e.getTarget();
1684
1685             this.dragCurrent = oDD;
1686
1687             var el = oDD.getEl();
1688
1689             // track start position
1690             this.startX = e.getPageX();
1691             this.startY = e.getPageY();
1692
1693             this.deltaX = this.startX - el.offsetLeft;
1694             this.deltaY = this.startY - el.offsetTop;
1695
1696             this.dragThreshMet = false;
1697
1698             this.clickTimeout = setTimeout(
1699                     function() {
1700                         var DDM = Roo.dd.DDM;
1701                         DDM.startDrag(DDM.startX, DDM.startY);
1702                     },
1703                     this.clickTimeThresh );
1704         },
1705
1706         /**
1707          * Fired when either the drag pixel threshol or the mousedown hold
1708          * time threshold has been met.
1709          * @method startDrag
1710          * @param x {int} the X position of the original mousedown
1711          * @param y {int} the Y position of the original mousedown
1712          * @static
1713          */
1714         startDrag: function(x, y) {
1715             clearTimeout(this.clickTimeout);
1716             if (this.dragCurrent) {
1717                 this.dragCurrent.b4StartDrag(x, y);
1718                 this.dragCurrent.startDrag(x, y);
1719             }
1720             this.dragThreshMet = true;
1721         },
1722
1723         /**
1724          * Internal function to handle the mouseup event.  Will be invoked
1725          * from the context of the document.
1726          * @method handleMouseUp
1727          * @param {Event} e the event
1728          * @private
1729          * @static
1730          */
1731         handleMouseUp: function(e) {
1732
1733             if(Roo.QuickTips){
1734                 Roo.QuickTips.enable();
1735             }
1736             if (! this.dragCurrent) {
1737                 return;
1738             }
1739
1740             clearTimeout(this.clickTimeout);
1741
1742             if (this.dragThreshMet) {
1743                 this.fireEvents(e, true);
1744             } else {
1745             }
1746
1747             this.stopDrag(e);
1748
1749             this.stopEvent(e);
1750         },
1751
1752         /**
1753          * Utility to stop event propagation and event default, if these
1754          * features are turned on.
1755          * @method stopEvent
1756          * @param {Event} e the event as returned by this.getEvent()
1757          * @static
1758          */
1759         stopEvent: function(e){
1760             if(this.stopPropagation) {
1761                 e.stopPropagation();
1762             }
1763
1764             if (this.preventDefault) {
1765                 e.preventDefault();
1766             }
1767         },
1768
1769         /**
1770          * Internal function to clean up event handlers after the drag
1771          * operation is complete
1772          * @method stopDrag
1773          * @param {Event} e the event
1774          * @private
1775          * @static
1776          */
1777         stopDrag: function(e) {
1778             // Fire the drag end event for the item that was dragged
1779             if (this.dragCurrent) {
1780                 if (this.dragThreshMet) {
1781                     this.dragCurrent.b4EndDrag(e);
1782                     this.dragCurrent.endDrag(e);
1783                 }
1784
1785                 this.dragCurrent.onMouseUp(e);
1786             }
1787
1788             this.dragCurrent = null;
1789             this.dragOvers = {};
1790         },
1791
1792         /**
1793          * Internal function to handle the mousemove event.  Will be invoked
1794          * from the context of the html element.
1795          *
1796          * @TODO figure out what we can do about mouse events lost when the
1797          * user drags objects beyond the window boundary.  Currently we can
1798          * detect this in internet explorer by verifying that the mouse is
1799          * down during the mousemove event.  Firefox doesn't give us the
1800          * button state on the mousemove event.
1801          * @method handleMouseMove
1802          * @param {Event} e the event
1803          * @private
1804          * @static
1805          */
1806         handleMouseMove: function(e) {
1807             if (! this.dragCurrent) {
1808                 return true;
1809             }
1810
1811             // var button = e.which || e.button;
1812
1813             // check for IE mouseup outside of page boundary
1814             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1815                 this.stopEvent(e);
1816                 return this.handleMouseUp(e);
1817             }
1818
1819             if (!this.dragThreshMet) {
1820                 var diffX = Math.abs(this.startX - e.getPageX());
1821                 var diffY = Math.abs(this.startY - e.getPageY());
1822                 if (diffX > this.clickPixelThresh ||
1823                             diffY > this.clickPixelThresh) {
1824                     this.startDrag(this.startX, this.startY);
1825                 }
1826             }
1827
1828             if (this.dragThreshMet) {
1829                 this.dragCurrent.b4Drag(e);
1830                 this.dragCurrent.onDrag(e);
1831                 if(!this.dragCurrent.moveOnly){
1832                     this.fireEvents(e, false);
1833                 }
1834             }
1835
1836             this.stopEvent(e);
1837
1838             return true;
1839         },
1840
1841         /**
1842          * Iterates over all of the DragDrop elements to find ones we are
1843          * hovering over or dropping on
1844          * @method fireEvents
1845          * @param {Event} e the event
1846          * @param {boolean} isDrop is this a drop op or a mouseover op?
1847          * @private
1848          * @static
1849          */
1850         fireEvents: function(e, isDrop) {
1851             var dc = this.dragCurrent;
1852
1853             // If the user did the mouse up outside of the window, we could
1854             // get here even though we have ended the drag.
1855             if (!dc || dc.isLocked()) {
1856                 return;
1857             }
1858
1859             var pt = e.getPoint();
1860
1861             // cache the previous dragOver array
1862             var oldOvers = [];
1863
1864             var outEvts   = [];
1865             var overEvts  = [];
1866             var dropEvts  = [];
1867             var enterEvts = [];
1868
1869             // Check to see if the object(s) we were hovering over is no longer
1870             // being hovered over so we can fire the onDragOut event
1871             for (var i in this.dragOvers) {
1872
1873                 var ddo = this.dragOvers[i];
1874
1875                 if (! this.isTypeOfDD(ddo)) {
1876                     continue;
1877                 }
1878
1879                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1880                     outEvts.push( ddo );
1881                 }
1882
1883                 oldOvers[i] = true;
1884                 delete this.dragOvers[i];
1885             }
1886
1887             for (var sGroup in dc.groups) {
1888
1889                 if ("string" != typeof sGroup) {
1890                     continue;
1891                 }
1892
1893                 for (i in this.ids[sGroup]) {
1894                     var oDD = this.ids[sGroup][i];
1895                     if (! this.isTypeOfDD(oDD)) {
1896                         continue;
1897                     }
1898
1899                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1900                         if (this.isOverTarget(pt, oDD, this.mode)) {
1901                             // look for drop interactions
1902                             if (isDrop) {
1903                                 dropEvts.push( oDD );
1904                             // look for drag enter and drag over interactions
1905                             } else {
1906
1907                                 // initial drag over: dragEnter fires
1908                                 if (!oldOvers[oDD.id]) {
1909                                     enterEvts.push( oDD );
1910                                 // subsequent drag overs: dragOver fires
1911                                 } else {
1912                                     overEvts.push( oDD );
1913                                 }
1914
1915                                 this.dragOvers[oDD.id] = oDD;
1916                             }
1917                         }
1918                     }
1919                 }
1920             }
1921
1922             if (this.mode) {
1923                 if (outEvts.length) {
1924                     dc.b4DragOut(e, outEvts);
1925                     dc.onDragOut(e, outEvts);
1926                 }
1927
1928                 if (enterEvts.length) {
1929                     dc.onDragEnter(e, enterEvts);
1930                 }
1931
1932                 if (overEvts.length) {
1933                     dc.b4DragOver(e, overEvts);
1934                     dc.onDragOver(e, overEvts);
1935                 }
1936
1937                 if (dropEvts.length) {
1938                     dc.b4DragDrop(e, dropEvts);
1939                     dc.onDragDrop(e, dropEvts);
1940                 }
1941
1942             } else {
1943                 // fire dragout events
1944                 var len = 0;
1945                 for (i=0, len=outEvts.length; i<len; ++i) {
1946                     dc.b4DragOut(e, outEvts[i].id);
1947                     dc.onDragOut(e, outEvts[i].id);
1948                 }
1949
1950                 // fire enter events
1951                 for (i=0,len=enterEvts.length; i<len; ++i) {
1952                     // dc.b4DragEnter(e, oDD.id);
1953                     dc.onDragEnter(e, enterEvts[i].id);
1954                 }
1955
1956                 // fire over events
1957                 for (i=0,len=overEvts.length; i<len; ++i) {
1958                     dc.b4DragOver(e, overEvts[i].id);
1959                     dc.onDragOver(e, overEvts[i].id);
1960                 }
1961
1962                 // fire drop events
1963                 for (i=0, len=dropEvts.length; i<len; ++i) {
1964                     dc.b4DragDrop(e, dropEvts[i].id);
1965                     dc.onDragDrop(e, dropEvts[i].id);
1966                 }
1967
1968             }
1969
1970             // notify about a drop that did not find a target
1971             if (isDrop && !dropEvts.length) {
1972                 dc.onInvalidDrop(e);
1973             }
1974
1975         },
1976
1977         /**
1978          * Helper function for getting the best match from the list of drag
1979          * and drop objects returned by the drag and drop events when we are
1980          * in INTERSECT mode.  It returns either the first object that the
1981          * cursor is over, or the object that has the greatest overlap with
1982          * the dragged element.
1983          * @method getBestMatch
1984          * @param  {DragDrop[]} dds The array of drag and drop objects
1985          * targeted
1986          * @return {DragDrop}       The best single match
1987          * @static
1988          */
1989         getBestMatch: function(dds) {
1990             var winner = null;
1991             // Return null if the input is not what we expect
1992             //if (!dds || !dds.length || dds.length == 0) {
1993                // winner = null;
1994             // If there is only one item, it wins
1995             //} else if (dds.length == 1) {
1996
1997             var len = dds.length;
1998
1999             if (len == 1) {
2000                 winner = dds[0];
2001             } else {
2002                 // Loop through the targeted items
2003                 for (var i=0; i<len; ++i) {
2004                     var dd = dds[i];
2005                     // If the cursor is over the object, it wins.  If the
2006                     // cursor is over multiple matches, the first one we come
2007                     // to wins.
2008                     if (dd.cursorIsOver) {
2009                         winner = dd;
2010                         break;
2011                     // Otherwise the object with the most overlap wins
2012                     } else {
2013                         if (!winner ||
2014                             winner.overlap.getArea() < dd.overlap.getArea()) {
2015                             winner = dd;
2016                         }
2017                     }
2018                 }
2019             }
2020
2021             return winner;
2022         },
2023
2024         /**
2025          * Refreshes the cache of the top-left and bottom-right points of the
2026          * drag and drop objects in the specified group(s).  This is in the
2027          * format that is stored in the drag and drop instance, so typical
2028          * usage is:
2029          * <code>
2030          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2031          * </code>
2032          * Alternatively:
2033          * <code>
2034          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2035          * </code>
2036          * @TODO this really should be an indexed array.  Alternatively this
2037          * method could accept both.
2038          * @method refreshCache
2039          * @param {Object} groups an associative array of groups to refresh
2040          * @static
2041          */
2042         refreshCache: function(groups) {
2043             for (var sGroup in groups) {
2044                 if ("string" != typeof sGroup) {
2045                     continue;
2046                 }
2047                 for (var i in this.ids[sGroup]) {
2048                     var oDD = this.ids[sGroup][i];
2049
2050                     if (this.isTypeOfDD(oDD)) {
2051                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2052                         var loc = this.getLocation(oDD);
2053                         if (loc) {
2054                             this.locationCache[oDD.id] = loc;
2055                         } else {
2056                             delete this.locationCache[oDD.id];
2057                             // this will unregister the drag and drop object if
2058                             // the element is not in a usable state
2059                             // oDD.unreg();
2060                         }
2061                     }
2062                 }
2063             }
2064         },
2065
2066         /**
2067          * This checks to make sure an element exists and is in the DOM.  The
2068          * main purpose is to handle cases where innerHTML is used to remove
2069          * drag and drop objects from the DOM.  IE provides an 'unspecified
2070          * error' when trying to access the offsetParent of such an element
2071          * @method verifyEl
2072          * @param {HTMLElement} el the element to check
2073          * @return {boolean} true if the element looks usable
2074          * @static
2075          */
2076         verifyEl: function(el) {
2077             if (el) {
2078                 var parent;
2079                 if(Roo.isIE){
2080                     try{
2081                         parent = el.offsetParent;
2082                     }catch(e){}
2083                 }else{
2084                     parent = el.offsetParent;
2085                 }
2086                 if (parent) {
2087                     return true;
2088                 }
2089             }
2090
2091             return false;
2092         },
2093
2094         /**
2095          * Returns a Region object containing the drag and drop element's position
2096          * and size, including the padding configured for it
2097          * @method getLocation
2098          * @param {DragDrop} oDD the drag and drop object to get the
2099          *                       location for
2100          * @return {Roo.lib.Region} a Region object representing the total area
2101          *                             the element occupies, including any padding
2102          *                             the instance is configured for.
2103          * @static
2104          */
2105         getLocation: function(oDD) {
2106             if (! this.isTypeOfDD(oDD)) {
2107                 return null;
2108             }
2109
2110             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2111
2112             try {
2113                 pos= Roo.lib.Dom.getXY(el);
2114             } catch (e) { }
2115
2116             if (!pos) {
2117                 return null;
2118             }
2119
2120             x1 = pos[0];
2121             x2 = x1 + el.offsetWidth;
2122             y1 = pos[1];
2123             y2 = y1 + el.offsetHeight;
2124
2125             t = y1 - oDD.padding[0];
2126             r = x2 + oDD.padding[1];
2127             b = y2 + oDD.padding[2];
2128             l = x1 - oDD.padding[3];
2129
2130             return new Roo.lib.Region( t, r, b, l );
2131         },
2132
2133         /**
2134          * Checks the cursor location to see if it over the target
2135          * @method isOverTarget
2136          * @param {Roo.lib.Point} pt The point to evaluate
2137          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2138          * @return {boolean} true if the mouse is over the target
2139          * @private
2140          * @static
2141          */
2142         isOverTarget: function(pt, oTarget, intersect) {
2143             // use cache if available
2144             var loc = this.locationCache[oTarget.id];
2145             if (!loc || !this.useCache) {
2146                 loc = this.getLocation(oTarget);
2147                 this.locationCache[oTarget.id] = loc;
2148
2149             }
2150
2151             if (!loc) {
2152                 return false;
2153             }
2154
2155             oTarget.cursorIsOver = loc.contains( pt );
2156
2157             // DragDrop is using this as a sanity check for the initial mousedown
2158             // in this case we are done.  In POINT mode, if the drag obj has no
2159             // contraints, we are also done. Otherwise we need to evaluate the
2160             // location of the target as related to the actual location of the
2161             // dragged element.
2162             var dc = this.dragCurrent;
2163             if (!dc || !dc.getTargetCoord ||
2164                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2165                 return oTarget.cursorIsOver;
2166             }
2167
2168             oTarget.overlap = null;
2169
2170             // Get the current location of the drag element, this is the
2171             // location of the mouse event less the delta that represents
2172             // where the original mousedown happened on the element.  We
2173             // need to consider constraints and ticks as well.
2174             var pos = dc.getTargetCoord(pt.x, pt.y);
2175
2176             var el = dc.getDragEl();
2177             var curRegion = new Roo.lib.Region( pos.y,
2178                                                    pos.x + el.offsetWidth,
2179                                                    pos.y + el.offsetHeight,
2180                                                    pos.x );
2181
2182             var overlap = curRegion.intersect(loc);
2183
2184             if (overlap) {
2185                 oTarget.overlap = overlap;
2186                 return (intersect) ? true : oTarget.cursorIsOver;
2187             } else {
2188                 return false;
2189             }
2190         },
2191
2192         /**
2193          * unload event handler
2194          * @method _onUnload
2195          * @private
2196          * @static
2197          */
2198         _onUnload: function(e, me) {
2199             Roo.dd.DragDropMgr.unregAll();
2200         },
2201
2202         /**
2203          * Cleans up the drag and drop events and objects.
2204          * @method unregAll
2205          * @private
2206          * @static
2207          */
2208         unregAll: function() {
2209
2210             if (this.dragCurrent) {
2211                 this.stopDrag();
2212                 this.dragCurrent = null;
2213             }
2214
2215             this._execOnAll("unreg", []);
2216
2217             for (i in this.elementCache) {
2218                 delete this.elementCache[i];
2219             }
2220
2221             this.elementCache = {};
2222             this.ids = {};
2223         },
2224
2225         /**
2226          * A cache of DOM elements
2227          * @property elementCache
2228          * @private
2229          * @static
2230          */
2231         elementCache: {},
2232
2233         /**
2234          * Get the wrapper for the DOM element specified
2235          * @method getElWrapper
2236          * @param {String} id the id of the element to get
2237          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2238          * @private
2239          * @deprecated This wrapper isn't that useful
2240          * @static
2241          */
2242         getElWrapper: function(id) {
2243             var oWrapper = this.elementCache[id];
2244             if (!oWrapper || !oWrapper.el) {
2245                 oWrapper = this.elementCache[id] =
2246                     new this.ElementWrapper(Roo.getDom(id));
2247             }
2248             return oWrapper;
2249         },
2250
2251         /**
2252          * Returns the actual DOM element
2253          * @method getElement
2254          * @param {String} id the id of the elment to get
2255          * @return {Object} The element
2256          * @deprecated use Roo.getDom instead
2257          * @static
2258          */
2259         getElement: function(id) {
2260             return Roo.getDom(id);
2261         },
2262
2263         /**
2264          * Returns the style property for the DOM element (i.e.,
2265          * document.getElById(id).style)
2266          * @method getCss
2267          * @param {String} id the id of the elment to get
2268          * @return {Object} The style property of the element
2269          * @deprecated use Roo.getDom instead
2270          * @static
2271          */
2272         getCss: function(id) {
2273             var el = Roo.getDom(id);
2274             return (el) ? el.style : null;
2275         },
2276
2277         /**
2278          * Inner class for cached elements
2279          * @class DragDropMgr.ElementWrapper
2280          * @for DragDropMgr
2281          * @private
2282          * @deprecated
2283          */
2284         ElementWrapper: function(el) {
2285                 /**
2286                  * The element
2287                  * @property el
2288                  */
2289                 this.el = el || null;
2290                 /**
2291                  * The element id
2292                  * @property id
2293                  */
2294                 this.id = this.el && el.id;
2295                 /**
2296                  * A reference to the style property
2297                  * @property css
2298                  */
2299                 this.css = this.el && el.style;
2300             },
2301
2302         /**
2303          * Returns the X position of an html element
2304          * @method getPosX
2305          * @param el the element for which to get the position
2306          * @return {int} the X coordinate
2307          * @for DragDropMgr
2308          * @deprecated use Roo.lib.Dom.getX instead
2309          * @static
2310          */
2311         getPosX: function(el) {
2312             return Roo.lib.Dom.getX(el);
2313         },
2314
2315         /**
2316          * Returns the Y position of an html element
2317          * @method getPosY
2318          * @param el the element for which to get the position
2319          * @return {int} the Y coordinate
2320          * @deprecated use Roo.lib.Dom.getY instead
2321          * @static
2322          */
2323         getPosY: function(el) {
2324             return Roo.lib.Dom.getY(el);
2325         },
2326
2327         /**
2328          * Swap two nodes.  In IE, we use the native method, for others we
2329          * emulate the IE behavior
2330          * @method swapNode
2331          * @param n1 the first node to swap
2332          * @param n2 the other node to swap
2333          * @static
2334          */
2335         swapNode: function(n1, n2) {
2336             if (n1.swapNode) {
2337                 n1.swapNode(n2);
2338             } else {
2339                 var p = n2.parentNode;
2340                 var s = n2.nextSibling;
2341
2342                 if (s == n1) {
2343                     p.insertBefore(n1, n2);
2344                 } else if (n2 == n1.nextSibling) {
2345                     p.insertBefore(n2, n1);
2346                 } else {
2347                     n1.parentNode.replaceChild(n2, n1);
2348                     p.insertBefore(n1, s);
2349                 }
2350             }
2351         },
2352
2353         /**
2354          * Returns the current scroll position
2355          * @method getScroll
2356          * @private
2357          * @static
2358          */
2359         getScroll: function () {
2360             var t, l, dde=document.documentElement, db=document.body;
2361             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2362                 t = dde.scrollTop;
2363                 l = dde.scrollLeft;
2364             } else if (db) {
2365                 t = db.scrollTop;
2366                 l = db.scrollLeft;
2367             } else {
2368
2369             }
2370             return { top: t, left: l };
2371         },
2372
2373         /**
2374          * Returns the specified element style property
2375          * @method getStyle
2376          * @param {HTMLElement} el          the element
2377          * @param {string}      styleProp   the style property
2378          * @return {string} The value of the style property
2379          * @deprecated use Roo.lib.Dom.getStyle
2380          * @static
2381          */
2382         getStyle: function(el, styleProp) {
2383             return Roo.fly(el).getStyle(styleProp);
2384         },
2385
2386         /**
2387          * Gets the scrollTop
2388          * @method getScrollTop
2389          * @return {int} the document's scrollTop
2390          * @static
2391          */
2392         getScrollTop: function () { return this.getScroll().top; },
2393
2394         /**
2395          * Gets the scrollLeft
2396          * @method getScrollLeft
2397          * @return {int} the document's scrollTop
2398          * @static
2399          */
2400         getScrollLeft: function () { return this.getScroll().left; },
2401
2402         /**
2403          * Sets the x/y position of an element to the location of the
2404          * target element.
2405          * @method moveToEl
2406          * @param {HTMLElement} moveEl      The element to move
2407          * @param {HTMLElement} targetEl    The position reference element
2408          * @static
2409          */
2410         moveToEl: function (moveEl, targetEl) {
2411             var aCoord = Roo.lib.Dom.getXY(targetEl);
2412             Roo.lib.Dom.setXY(moveEl, aCoord);
2413         },
2414
2415         /**
2416          * Numeric array sort function
2417          * @method numericSort
2418          * @static
2419          */
2420         numericSort: function(a, b) { return (a - b); },
2421
2422         /**
2423          * Internal counter
2424          * @property _timeoutCount
2425          * @private
2426          * @static
2427          */
2428         _timeoutCount: 0,
2429
2430         /**
2431          * Trying to make the load order less important.  Without this we get
2432          * an error if this file is loaded before the Event Utility.
2433          * @method _addListeners
2434          * @private
2435          * @static
2436          */
2437         _addListeners: function() {
2438             var DDM = Roo.dd.DDM;
2439             if ( Roo.lib.Event && document ) {
2440                 DDM._onLoad();
2441             } else {
2442                 if (DDM._timeoutCount > 2000) {
2443                 } else {
2444                     setTimeout(DDM._addListeners, 10);
2445                     if (document && document.body) {
2446                         DDM._timeoutCount += 1;
2447                     }
2448                 }
2449             }
2450         },
2451
2452         /**
2453          * Recursively searches the immediate parent and all child nodes for
2454          * the handle element in order to determine wheter or not it was
2455          * clicked.
2456          * @method handleWasClicked
2457          * @param node the html element to inspect
2458          * @static
2459          */
2460         handleWasClicked: function(node, id) {
2461             if (this.isHandle(id, node.id)) {
2462                 return true;
2463             } else {
2464                 // check to see if this is a text node child of the one we want
2465                 var p = node.parentNode;
2466
2467                 while (p) {
2468                     if (this.isHandle(id, p.id)) {
2469                         return true;
2470                     } else {
2471                         p = p.parentNode;
2472                     }
2473                 }
2474             }
2475
2476             return false;
2477         }
2478
2479     };
2480
2481 }();
2482
2483 // shorter alias, save a few bytes
2484 Roo.dd.DDM = Roo.dd.DragDropMgr;
2485 Roo.dd.DDM._addListeners();
2486
2487 }/*
2488  * Based on:
2489  * Ext JS Library 1.1.1
2490  * Copyright(c) 2006-2007, Ext JS, LLC.
2491  *
2492  * Originally Released Under LGPL - original licence link has changed is not relivant.
2493  *
2494  * Fork - LGPL
2495  * <script type="text/javascript">
2496  */
2497
2498 /**
2499  * @class Roo.dd.DD
2500  * A DragDrop implementation where the linked element follows the
2501  * mouse cursor during a drag.
2502  * @extends Roo.dd.DragDrop
2503  * @constructor
2504  * @param {String} id the id of the linked element
2505  * @param {String} sGroup the group of related DragDrop items
2506  * @param {object} config an object containing configurable attributes
2507  *                Valid properties for DD:
2508  *                    scroll
2509  */
2510 Roo.dd.DD = function(id, sGroup, config) {
2511     if (id) {
2512         this.init(id, sGroup, config);
2513     }
2514 };
2515
2516 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2517
2518     /**
2519      * When set to true, the utility automatically tries to scroll the browser
2520      * window wehn a drag and drop element is dragged near the viewport boundary.
2521      * Defaults to true.
2522      * @property scroll
2523      * @type boolean
2524      */
2525     scroll: true,
2526
2527     /**
2528      * Sets the pointer offset to the distance between the linked element's top
2529      * left corner and the location the element was clicked
2530      * @method autoOffset
2531      * @param {int} iPageX the X coordinate of the click
2532      * @param {int} iPageY the Y coordinate of the click
2533      */
2534     autoOffset: function(iPageX, iPageY) {
2535         var x = iPageX - this.startPageX;
2536         var y = iPageY - this.startPageY;
2537         this.setDelta(x, y);
2538     },
2539
2540     /**
2541      * Sets the pointer offset.  You can call this directly to force the
2542      * offset to be in a particular location (e.g., pass in 0,0 to set it
2543      * to the center of the object)
2544      * @method setDelta
2545      * @param {int} iDeltaX the distance from the left
2546      * @param {int} iDeltaY the distance from the top
2547      */
2548     setDelta: function(iDeltaX, iDeltaY) {
2549         this.deltaX = iDeltaX;
2550         this.deltaY = iDeltaY;
2551     },
2552
2553     /**
2554      * Sets the drag element to the location of the mousedown or click event,
2555      * maintaining the cursor location relative to the location on the element
2556      * that was clicked.  Override this if you want to place the element in a
2557      * location other than where the cursor is.
2558      * @method setDragElPos
2559      * @param {int} iPageX the X coordinate of the mousedown or drag event
2560      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2561      */
2562     setDragElPos: function(iPageX, iPageY) {
2563         // the first time we do this, we are going to check to make sure
2564         // the element has css positioning
2565
2566         var el = this.getDragEl();
2567         this.alignElWithMouse(el, iPageX, iPageY);
2568     },
2569
2570     /**
2571      * Sets the element to the location of the mousedown or click event,
2572      * maintaining the cursor location relative to the location on the element
2573      * that was clicked.  Override this if you want to place the element in a
2574      * location other than where the cursor is.
2575      * @method alignElWithMouse
2576      * @param {HTMLElement} el the element to move
2577      * @param {int} iPageX the X coordinate of the mousedown or drag event
2578      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2579      */
2580     alignElWithMouse: function(el, iPageX, iPageY) {
2581         var oCoord = this.getTargetCoord(iPageX, iPageY);
2582         var fly = el.dom ? el : Roo.fly(el);
2583         if (!this.deltaSetXY) {
2584             var aCoord = [oCoord.x, oCoord.y];
2585             fly.setXY(aCoord);
2586             var newLeft = fly.getLeft(true);
2587             var newTop  = fly.getTop(true);
2588             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2589         } else {
2590             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2591         }
2592
2593         this.cachePosition(oCoord.x, oCoord.y);
2594         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2595         return oCoord;
2596     },
2597
2598     /**
2599      * Saves the most recent position so that we can reset the constraints and
2600      * tick marks on-demand.  We need to know this so that we can calculate the
2601      * number of pixels the element is offset from its original position.
2602      * @method cachePosition
2603      * @param iPageX the current x position (optional, this just makes it so we
2604      * don't have to look it up again)
2605      * @param iPageY the current y position (optional, this just makes it so we
2606      * don't have to look it up again)
2607      */
2608     cachePosition: function(iPageX, iPageY) {
2609         if (iPageX) {
2610             this.lastPageX = iPageX;
2611             this.lastPageY = iPageY;
2612         } else {
2613             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2614             this.lastPageX = aCoord[0];
2615             this.lastPageY = aCoord[1];
2616         }
2617     },
2618
2619     /**
2620      * Auto-scroll the window if the dragged object has been moved beyond the
2621      * visible window boundary.
2622      * @method autoScroll
2623      * @param {int} x the drag element's x position
2624      * @param {int} y the drag element's y position
2625      * @param {int} h the height of the drag element
2626      * @param {int} w the width of the drag element
2627      * @private
2628      */
2629     autoScroll: function(x, y, h, w) {
2630
2631         if (this.scroll) {
2632             // The client height
2633             var clientH = Roo.lib.Dom.getViewWidth();
2634
2635             // The client width
2636             var clientW = Roo.lib.Dom.getViewHeight();
2637
2638             // The amt scrolled down
2639             var st = this.DDM.getScrollTop();
2640
2641             // The amt scrolled right
2642             var sl = this.DDM.getScrollLeft();
2643
2644             // Location of the bottom of the element
2645             var bot = h + y;
2646
2647             // Location of the right of the element
2648             var right = w + x;
2649
2650             // The distance from the cursor to the bottom of the visible area,
2651             // adjusted so that we don't scroll if the cursor is beyond the
2652             // element drag constraints
2653             var toBot = (clientH + st - y - this.deltaY);
2654
2655             // The distance from the cursor to the right of the visible area
2656             var toRight = (clientW + sl - x - this.deltaX);
2657
2658
2659             // How close to the edge the cursor must be before we scroll
2660             // var thresh = (document.all) ? 100 : 40;
2661             var thresh = 40;
2662
2663             // How many pixels to scroll per autoscroll op.  This helps to reduce
2664             // clunky scrolling. IE is more sensitive about this ... it needs this
2665             // value to be higher.
2666             var scrAmt = (document.all) ? 80 : 30;
2667
2668             // Scroll down if we are near the bottom of the visible page and the
2669             // obj extends below the crease
2670             if ( bot > clientH && toBot < thresh ) {
2671                 window.scrollTo(sl, st + scrAmt);
2672             }
2673
2674             // Scroll up if the window is scrolled down and the top of the object
2675             // goes above the top border
2676             if ( y < st && st > 0 && y - st < thresh ) {
2677                 window.scrollTo(sl, st - scrAmt);
2678             }
2679
2680             // Scroll right if the obj is beyond the right border and the cursor is
2681             // near the border.
2682             if ( right > clientW && toRight < thresh ) {
2683                 window.scrollTo(sl + scrAmt, st);
2684             }
2685
2686             // Scroll left if the window has been scrolled to the right and the obj
2687             // extends past the left border
2688             if ( x < sl && sl > 0 && x - sl < thresh ) {
2689                 window.scrollTo(sl - scrAmt, st);
2690             }
2691         }
2692     },
2693
2694     /**
2695      * Finds the location the element should be placed if we want to move
2696      * it to where the mouse location less the click offset would place us.
2697      * @method getTargetCoord
2698      * @param {int} iPageX the X coordinate of the click
2699      * @param {int} iPageY the Y coordinate of the click
2700      * @return an object that contains the coordinates (Object.x and Object.y)
2701      * @private
2702      */
2703     getTargetCoord: function(iPageX, iPageY) {
2704
2705
2706         var x = iPageX - this.deltaX;
2707         var y = iPageY - this.deltaY;
2708
2709         if (this.constrainX) {
2710             if (x < this.minX) { x = this.minX; }
2711             if (x > this.maxX) { x = this.maxX; }
2712         }
2713
2714         if (this.constrainY) {
2715             if (y < this.minY) { y = this.minY; }
2716             if (y > this.maxY) { y = this.maxY; }
2717         }
2718
2719         x = this.getTick(x, this.xTicks);
2720         y = this.getTick(y, this.yTicks);
2721
2722
2723         return {x:x, y:y};
2724     },
2725
2726     /*
2727      * Sets up config options specific to this class. Overrides
2728      * Roo.dd.DragDrop, but all versions of this method through the
2729      * inheritance chain are called
2730      */
2731     applyConfig: function() {
2732         Roo.dd.DD.superclass.applyConfig.call(this);
2733         this.scroll = (this.config.scroll !== false);
2734     },
2735
2736     /*
2737      * Event that fires prior to the onMouseDown event.  Overrides
2738      * Roo.dd.DragDrop.
2739      */
2740     b4MouseDown: function(e) {
2741         // this.resetConstraints();
2742         this.autoOffset(e.getPageX(),
2743                             e.getPageY());
2744     },
2745
2746     /*
2747      * Event that fires prior to the onDrag event.  Overrides
2748      * Roo.dd.DragDrop.
2749      */
2750     b4Drag: function(e) {
2751         this.setDragElPos(e.getPageX(),
2752                             e.getPageY());
2753     },
2754
2755     toString: function() {
2756         return ("DD " + this.id);
2757     }
2758
2759     //////////////////////////////////////////////////////////////////////////
2760     // Debugging ygDragDrop events that can be overridden
2761     //////////////////////////////////////////////////////////////////////////
2762     /*
2763     startDrag: function(x, y) {
2764     },
2765
2766     onDrag: function(e) {
2767     },
2768
2769     onDragEnter: function(e, id) {
2770     },
2771
2772     onDragOver: function(e, id) {
2773     },
2774
2775     onDragOut: function(e, id) {
2776     },
2777
2778     onDragDrop: function(e, id) {
2779     },
2780
2781     endDrag: function(e) {
2782     }
2783
2784     */
2785
2786 });/*
2787  * Based on:
2788  * Ext JS Library 1.1.1
2789  * Copyright(c) 2006-2007, Ext JS, LLC.
2790  *
2791  * Originally Released Under LGPL - original licence link has changed is not relivant.
2792  *
2793  * Fork - LGPL
2794  * <script type="text/javascript">
2795  */
2796
2797 /**
2798  * @class Roo.dd.DDProxy
2799  * A DragDrop implementation that inserts an empty, bordered div into
2800  * the document that follows the cursor during drag operations.  At the time of
2801  * the click, the frame div is resized to the dimensions of the linked html
2802  * element, and moved to the exact location of the linked element.
2803  *
2804  * References to the "frame" element refer to the single proxy element that
2805  * was created to be dragged in place of all DDProxy elements on the
2806  * page.
2807  *
2808  * @extends Roo.dd.DD
2809  * @constructor
2810  * @param {String} id the id of the linked html element
2811  * @param {String} sGroup the group of related DragDrop objects
2812  * @param {object} config an object containing configurable attributes
2813  *                Valid properties for DDProxy in addition to those in DragDrop:
2814  *                   resizeFrame, centerFrame, dragElId
2815  */
2816 Roo.dd.DDProxy = function(id, sGroup, config) {
2817     if (id) {
2818         this.init(id, sGroup, config);
2819         this.initFrame();
2820     }
2821 };
2822
2823 /**
2824  * The default drag frame div id
2825  * @property Roo.dd.DDProxy.dragElId
2826  * @type String
2827  * @static
2828  */
2829 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2830
2831 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2832
2833     /**
2834      * By default we resize the drag frame to be the same size as the element
2835      * we want to drag (this is to get the frame effect).  We can turn it off
2836      * if we want a different behavior.
2837      * @property resizeFrame
2838      * @type boolean
2839      */
2840     resizeFrame: true,
2841
2842     /**
2843      * By default the frame is positioned exactly where the drag element is, so
2844      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2845      * you do not have constraints on the obj is to have the drag frame centered
2846      * around the cursor.  Set centerFrame to true for this effect.
2847      * @property centerFrame
2848      * @type boolean
2849      */
2850     centerFrame: false,
2851
2852     /**
2853      * Creates the proxy element if it does not yet exist
2854      * @method createFrame
2855      */
2856     createFrame: function() {
2857         var self = this;
2858         var body = document.body;
2859
2860         if (!body || !body.firstChild) {
2861             setTimeout( function() { self.createFrame(); }, 50 );
2862             return;
2863         }
2864
2865         var div = this.getDragEl();
2866
2867         if (!div) {
2868             div    = document.createElement("div");
2869             div.id = this.dragElId;
2870             var s  = div.style;
2871
2872             s.position   = "absolute";
2873             s.visibility = "hidden";
2874             s.cursor     = "move";
2875             s.border     = "2px solid #aaa";
2876             s.zIndex     = 999;
2877
2878             // appendChild can blow up IE if invoked prior to the window load event
2879             // while rendering a table.  It is possible there are other scenarios
2880             // that would cause this to happen as well.
2881             body.insertBefore(div, body.firstChild);
2882         }
2883     },
2884
2885     /**
2886      * Initialization for the drag frame element.  Must be called in the
2887      * constructor of all subclasses
2888      * @method initFrame
2889      */
2890     initFrame: function() {
2891         this.createFrame();
2892     },
2893
2894     applyConfig: function() {
2895         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2896
2897         this.resizeFrame = (this.config.resizeFrame !== false);
2898         this.centerFrame = (this.config.centerFrame);
2899         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2900     },
2901
2902     /**
2903      * Resizes the drag frame to the dimensions of the clicked object, positions
2904      * it over the object, and finally displays it
2905      * @method showFrame
2906      * @param {int} iPageX X click position
2907      * @param {int} iPageY Y click position
2908      * @private
2909      */
2910     showFrame: function(iPageX, iPageY) {
2911         var el = this.getEl();
2912         var dragEl = this.getDragEl();
2913         var s = dragEl.style;
2914
2915         this._resizeProxy();
2916
2917         if (this.centerFrame) {
2918             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2919                            Math.round(parseInt(s.height, 10)/2) );
2920         }
2921
2922         this.setDragElPos(iPageX, iPageY);
2923
2924         Roo.fly(dragEl).show();
2925     },
2926
2927     /**
2928      * The proxy is automatically resized to the dimensions of the linked
2929      * element when a drag is initiated, unless resizeFrame is set to false
2930      * @method _resizeProxy
2931      * @private
2932      */
2933     _resizeProxy: function() {
2934         if (this.resizeFrame) {
2935             var el = this.getEl();
2936             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2937         }
2938     },
2939
2940     // overrides Roo.dd.DragDrop
2941     b4MouseDown: function(e) {
2942         var x = e.getPageX();
2943         var y = e.getPageY();
2944         this.autoOffset(x, y);
2945         this.setDragElPos(x, y);
2946     },
2947
2948     // overrides Roo.dd.DragDrop
2949     b4StartDrag: function(x, y) {
2950         // show the drag frame
2951         this.showFrame(x, y);
2952     },
2953
2954     // overrides Roo.dd.DragDrop
2955     b4EndDrag: function(e) {
2956         Roo.fly(this.getDragEl()).hide();
2957     },
2958
2959     // overrides Roo.dd.DragDrop
2960     // By default we try to move the element to the last location of the frame.
2961     // This is so that the default behavior mirrors that of Roo.dd.DD.
2962     endDrag: function(e) {
2963
2964         var lel = this.getEl();
2965         var del = this.getDragEl();
2966
2967         // Show the drag frame briefly so we can get its position
2968         del.style.visibility = "";
2969
2970         this.beforeMove();
2971         // Hide the linked element before the move to get around a Safari
2972         // rendering bug.
2973         lel.style.visibility = "hidden";
2974         Roo.dd.DDM.moveToEl(lel, del);
2975         del.style.visibility = "hidden";
2976         lel.style.visibility = "";
2977
2978         this.afterDrag();
2979     },
2980
2981     beforeMove : function(){
2982
2983     },
2984
2985     afterDrag : function(){
2986
2987     },
2988
2989     toString: function() {
2990         return ("DDProxy " + this.id);
2991     }
2992
2993 });
2994 /*
2995  * Based on:
2996  * Ext JS Library 1.1.1
2997  * Copyright(c) 2006-2007, Ext JS, LLC.
2998  *
2999  * Originally Released Under LGPL - original licence link has changed is not relivant.
3000  *
3001  * Fork - LGPL
3002  * <script type="text/javascript">
3003  */
3004
3005  /**
3006  * @class Roo.dd.DDTarget
3007  * A DragDrop implementation that does not move, but can be a drop
3008  * target.  You would get the same result by simply omitting implementation
3009  * for the event callbacks, but this way we reduce the processing cost of the
3010  * event listener and the callbacks.
3011  * @extends Roo.dd.DragDrop
3012  * @constructor
3013  * @param {String} id the id of the element that is a drop target
3014  * @param {String} sGroup the group of related DragDrop objects
3015  * @param {object} config an object containing configurable attributes
3016  *                 Valid properties for DDTarget in addition to those in
3017  *                 DragDrop:
3018  *                    none
3019  */
3020 Roo.dd.DDTarget = function(id, sGroup, config) {
3021     if (id) {
3022         this.initTarget(id, sGroup, config);
3023     }
3024     if (config.listeners || config.events) { 
3025        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
3026             listeners : config.listeners || {}, 
3027             events : config.events || {} 
3028         });    
3029     }
3030 };
3031
3032 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3033 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3034     toString: function() {
3035         return ("DDTarget " + this.id);
3036     }
3037 });
3038 /*
3039  * Based on:
3040  * Ext JS Library 1.1.1
3041  * Copyright(c) 2006-2007, Ext JS, LLC.
3042  *
3043  * Originally Released Under LGPL - original licence link has changed is not relivant.
3044  *
3045  * Fork - LGPL
3046  * <script type="text/javascript">
3047  */
3048  
3049
3050 /**
3051  * @class Roo.dd.ScrollManager
3052  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3053  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3054  * @singleton
3055  */
3056 Roo.dd.ScrollManager = function(){
3057     var ddm = Roo.dd.DragDropMgr;
3058     var els = {};
3059     var dragEl = null;
3060     var proc = {};
3061     
3062     
3063     
3064     var onStop = function(e){
3065         dragEl = null;
3066         clearProc();
3067     };
3068     
3069     var triggerRefresh = function(){
3070         if(ddm.dragCurrent){
3071              ddm.refreshCache(ddm.dragCurrent.groups);
3072         }
3073     };
3074     
3075     var doScroll = function(){
3076         if(ddm.dragCurrent){
3077             var dds = Roo.dd.ScrollManager;
3078             if(!dds.animate){
3079                 if(proc.el.scroll(proc.dir, dds.increment)){
3080                     triggerRefresh();
3081                 }
3082             }else{
3083                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3084             }
3085         }
3086     };
3087     
3088     var clearProc = function(){
3089         if(proc.id){
3090             clearInterval(proc.id);
3091         }
3092         proc.id = 0;
3093         proc.el = null;
3094         proc.dir = "";
3095     };
3096     
3097     var startProc = function(el, dir){
3098          Roo.log('scroll startproc');
3099         clearProc();
3100         proc.el = el;
3101         proc.dir = dir;
3102         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3103     };
3104     
3105     var onFire = function(e, isDrop){
3106        
3107         if(isDrop || !ddm.dragCurrent){ return; }
3108         var dds = Roo.dd.ScrollManager;
3109         if(!dragEl || dragEl != ddm.dragCurrent){
3110             dragEl = ddm.dragCurrent;
3111             // refresh regions on drag start
3112             dds.refreshCache();
3113         }
3114         
3115         var xy = Roo.lib.Event.getXY(e);
3116         var pt = new Roo.lib.Point(xy[0], xy[1]);
3117         for(var id in els){
3118             var el = els[id], r = el._region;
3119             if(r && r.contains(pt) && el.isScrollable()){
3120                 if(r.bottom - pt.y <= dds.thresh){
3121                     if(proc.el != el){
3122                         startProc(el, "down");
3123                     }
3124                     return;
3125                 }else if(r.right - pt.x <= dds.thresh){
3126                     if(proc.el != el){
3127                         startProc(el, "left");
3128                     }
3129                     return;
3130                 }else if(pt.y - r.top <= dds.thresh){
3131                     if(proc.el != el){
3132                         startProc(el, "up");
3133                     }
3134                     return;
3135                 }else if(pt.x - r.left <= dds.thresh){
3136                     if(proc.el != el){
3137                         startProc(el, "right");
3138                     }
3139                     return;
3140                 }
3141             }
3142         }
3143         clearProc();
3144     };
3145     
3146     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3147     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3148     
3149     return {
3150         /**
3151          * Registers new overflow element(s) to auto scroll
3152          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3153          */
3154         register : function(el){
3155             if(el instanceof Array){
3156                 for(var i = 0, len = el.length; i < len; i++) {
3157                         this.register(el[i]);
3158                 }
3159             }else{
3160                 el = Roo.get(el);
3161                 els[el.id] = el;
3162             }
3163             Roo.dd.ScrollManager.els = els;
3164         },
3165         
3166         /**
3167          * Unregisters overflow element(s) so they are no longer scrolled
3168          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3169          */
3170         unregister : function(el){
3171             if(el instanceof Array){
3172                 for(var i = 0, len = el.length; i < len; i++) {
3173                         this.unregister(el[i]);
3174                 }
3175             }else{
3176                 el = Roo.get(el);
3177                 delete els[el.id];
3178             }
3179         },
3180         
3181         /**
3182          * The number of pixels from the edge of a container the pointer needs to be to 
3183          * trigger scrolling (defaults to 25)
3184          * @type Number
3185          */
3186         thresh : 25,
3187         
3188         /**
3189          * The number of pixels to scroll in each scroll increment (defaults to 50)
3190          * @type Number
3191          */
3192         increment : 100,
3193         
3194         /**
3195          * The frequency of scrolls in milliseconds (defaults to 500)
3196          * @type Number
3197          */
3198         frequency : 500,
3199         
3200         /**
3201          * True to animate the scroll (defaults to true)
3202          * @type Boolean
3203          */
3204         animate: true,
3205         
3206         /**
3207          * The animation duration in seconds - 
3208          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3209          * @type Number
3210          */
3211         animDuration: .4,
3212         
3213         /**
3214          * Manually trigger a cache refresh.
3215          */
3216         refreshCache : function(){
3217             for(var id in els){
3218                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3219                     els[id]._region = els[id].getRegion();
3220                 }
3221             }
3222         }
3223     };
3224 }();/*
3225  * Based on:
3226  * Ext JS Library 1.1.1
3227  * Copyright(c) 2006-2007, Ext JS, LLC.
3228  *
3229  * Originally Released Under LGPL - original licence link has changed is not relivant.
3230  *
3231  * Fork - LGPL
3232  * <script type="text/javascript">
3233  */
3234  
3235
3236 /**
3237  * @class Roo.dd.Registry
3238  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3239  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3240  * @singleton
3241  */
3242 Roo.dd.Registry = function(){
3243     var elements = {}; 
3244     var handles = {}; 
3245     var autoIdSeed = 0;
3246
3247     var getId = function(el, autogen){
3248         if(typeof el == "string"){
3249             return el;
3250         }
3251         var id = el.id;
3252         if(!id && autogen !== false){
3253             id = "roodd-" + (++autoIdSeed);
3254             el.id = id;
3255         }
3256         return id;
3257     };
3258     
3259     return {
3260     /**
3261      * Register a drag drop element
3262      * @param {String|HTMLElement} element The id or DOM node to register
3263      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3264      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3265      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3266      * populated in the data object (if applicable):
3267      * <pre>
3268 Value      Description<br />
3269 ---------  ------------------------------------------<br />
3270 handles    Array of DOM nodes that trigger dragging<br />
3271            for the element being registered<br />
3272 isHandle   True if the element passed in triggers<br />
3273            dragging itself, else false
3274 </pre>
3275      */
3276         register : function(el, data){
3277             data = data || {};
3278             if(typeof el == "string"){
3279                 el = document.getElementById(el);
3280             }
3281             data.ddel = el;
3282             elements[getId(el)] = data;
3283             if(data.isHandle !== false){
3284                 handles[data.ddel.id] = data;
3285             }
3286             if(data.handles){
3287                 var hs = data.handles;
3288                 for(var i = 0, len = hs.length; i < len; i++){
3289                         handles[getId(hs[i])] = data;
3290                 }
3291             }
3292         },
3293
3294     /**
3295      * Unregister a drag drop element
3296      * @param {String|HTMLElement}  element The id or DOM node to unregister
3297      */
3298         unregister : function(el){
3299             var id = getId(el, false);
3300             var data = elements[id];
3301             if(data){
3302                 delete elements[id];
3303                 if(data.handles){
3304                     var hs = data.handles;
3305                     for(var i = 0, len = hs.length; i < len; i++){
3306                         delete handles[getId(hs[i], false)];
3307                     }
3308                 }
3309             }
3310         },
3311
3312     /**
3313      * Returns the handle registered for a DOM Node by id
3314      * @param {String|HTMLElement} id The DOM node or id to look up
3315      * @return {Object} handle The custom handle data
3316      */
3317         getHandle : function(id){
3318             if(typeof id != "string"){ // must be element?
3319                 id = id.id;
3320             }
3321             return handles[id];
3322         },
3323
3324     /**
3325      * Returns the handle that is registered for the DOM node that is the target of the event
3326      * @param {Event} e The event
3327      * @return {Object} handle The custom handle data
3328      */
3329         getHandleFromEvent : function(e){
3330             var t = Roo.lib.Event.getTarget(e);
3331             return t ? handles[t.id] : null;
3332         },
3333
3334     /**
3335      * Returns a custom data object that is registered for a DOM node by id
3336      * @param {String|HTMLElement} id The DOM node or id to look up
3337      * @return {Object} data The custom data
3338      */
3339         getTarget : function(id){
3340             if(typeof id != "string"){ // must be element?
3341                 id = id.id;
3342             }
3343             return elements[id];
3344         },
3345
3346     /**
3347      * Returns a custom data object that is registered for the DOM node that is the target of the event
3348      * @param {Event} e The event
3349      * @return {Object} data The custom data
3350      */
3351         getTargetFromEvent : function(e){
3352             var t = Roo.lib.Event.getTarget(e);
3353             return t ? elements[t.id] || handles[t.id] : null;
3354         }
3355     };
3356 }();/*
3357  * Based on:
3358  * Ext JS Library 1.1.1
3359  * Copyright(c) 2006-2007, Ext JS, LLC.
3360  *
3361  * Originally Released Under LGPL - original licence link has changed is not relivant.
3362  *
3363  * Fork - LGPL
3364  * <script type="text/javascript">
3365  */
3366  
3367
3368 /**
3369  * @class Roo.dd.StatusProxy
3370  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3371  * default drag proxy used by all Roo.dd components.
3372  * @constructor
3373  * @param {Object} config
3374  */
3375 Roo.dd.StatusProxy = function(config){
3376     Roo.apply(this, config);
3377     this.id = this.id || Roo.id();
3378     this.el = new Roo.Layer({
3379         dh: {
3380             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3381                 {tag: "div", cls: "x-dd-drop-icon"},
3382                 {tag: "div", cls: "x-dd-drag-ghost"}
3383             ]
3384         }, 
3385         shadow: !config || config.shadow !== false
3386     });
3387     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3388     this.dropStatus = this.dropNotAllowed;
3389 };
3390
3391 Roo.dd.StatusProxy.prototype = {
3392     /**
3393      * @cfg {String} dropAllowed
3394      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3395      */
3396     dropAllowed : "x-dd-drop-ok",
3397     /**
3398      * @cfg {String} dropNotAllowed
3399      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3400      */
3401     dropNotAllowed : "x-dd-drop-nodrop",
3402
3403     /**
3404      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3405      * over the current target element.
3406      * @param {String} cssClass The css class for the new drop status indicator image
3407      */
3408     setStatus : function(cssClass){
3409         cssClass = cssClass || this.dropNotAllowed;
3410         if(this.dropStatus != cssClass){
3411             this.el.replaceClass(this.dropStatus, cssClass);
3412             this.dropStatus = cssClass;
3413         }
3414     },
3415
3416     /**
3417      * Resets the status indicator to the default dropNotAllowed value
3418      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3419      */
3420     reset : function(clearGhost){
3421         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3422         this.dropStatus = this.dropNotAllowed;
3423         if(clearGhost){
3424             this.ghost.update("");
3425         }
3426     },
3427
3428     /**
3429      * Updates the contents of the ghost element
3430      * @param {String} html The html that will replace the current innerHTML of the ghost element
3431      */
3432     update : function(html){
3433         if(typeof html == "string"){
3434             this.ghost.update(html);
3435         }else{
3436             this.ghost.update("");
3437             html.style.margin = "0";
3438             this.ghost.dom.appendChild(html);
3439         }
3440         // ensure float = none set?? cant remember why though.
3441         var el = this.ghost.dom.firstChild;
3442                 if(el){
3443                         Roo.fly(el).setStyle('float', 'none');
3444                 }
3445     },
3446     
3447     /**
3448      * Returns the underlying proxy {@link Roo.Layer}
3449      * @return {Roo.Layer} el
3450     */
3451     getEl : function(){
3452         return this.el;
3453     },
3454
3455     /**
3456      * Returns the ghost element
3457      * @return {Roo.Element} el
3458      */
3459     getGhost : function(){
3460         return this.ghost;
3461     },
3462
3463     /**
3464      * Hides the proxy
3465      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3466      */
3467     hide : function(clear){
3468         this.el.hide();
3469         if(clear){
3470             this.reset(true);
3471         }
3472     },
3473
3474     /**
3475      * Stops the repair animation if it's currently running
3476      */
3477     stop : function(){
3478         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3479             this.anim.stop();
3480         }
3481     },
3482
3483     /**
3484      * Displays this proxy
3485      */
3486     show : function(){
3487         this.el.show();
3488     },
3489
3490     /**
3491      * Force the Layer to sync its shadow and shim positions to the element
3492      */
3493     sync : function(){
3494         this.el.sync();
3495     },
3496
3497     /**
3498      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3499      * invalid drop operation by the item being dragged.
3500      * @param {Array} xy The XY position of the element ([x, y])
3501      * @param {Function} callback The function to call after the repair is complete
3502      * @param {Object} scope The scope in which to execute the callback
3503      */
3504     repair : function(xy, callback, scope){
3505         this.callback = callback;
3506         this.scope = scope;
3507         if(xy && this.animRepair !== false){
3508             this.el.addClass("x-dd-drag-repair");
3509             this.el.hideUnders(true);
3510             this.anim = this.el.shift({
3511                 duration: this.repairDuration || .5,
3512                 easing: 'easeOut',
3513                 xy: xy,
3514                 stopFx: true,
3515                 callback: this.afterRepair,
3516                 scope: this
3517             });
3518         }else{
3519             this.afterRepair();
3520         }
3521     },
3522
3523     // private
3524     afterRepair : function(){
3525         this.hide(true);
3526         if(typeof this.callback == "function"){
3527             this.callback.call(this.scope || this);
3528         }
3529         this.callback = null;
3530         this.scope = null;
3531     }
3532 };/*
3533  * Based on:
3534  * Ext JS Library 1.1.1
3535  * Copyright(c) 2006-2007, Ext JS, LLC.
3536  *
3537  * Originally Released Under LGPL - original licence link has changed is not relivant.
3538  *
3539  * Fork - LGPL
3540  * <script type="text/javascript">
3541  */
3542
3543 /**
3544  * @class Roo.dd.DragSource
3545  * @extends Roo.dd.DDProxy
3546  * A simple class that provides the basic implementation needed to make any element draggable.
3547  * @constructor
3548  * @param {String/HTMLElement/Element} el The container element
3549  * @param {Object} config
3550  */
3551 Roo.dd.DragSource = function(el, config){
3552     this.el = Roo.get(el);
3553     this.dragData = {};
3554     
3555     Roo.apply(this, config);
3556     
3557     if(!this.proxy){
3558         this.proxy = new Roo.dd.StatusProxy();
3559     }
3560
3561     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3562           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3563     
3564     this.dragging = false;
3565 };
3566
3567 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3568     /**
3569      * @cfg {String} dropAllowed
3570      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3571      */
3572     dropAllowed : "x-dd-drop-ok",
3573     /**
3574      * @cfg {String} dropNotAllowed
3575      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3576      */
3577     dropNotAllowed : "x-dd-drop-nodrop",
3578
3579     /**
3580      * Returns the data object associated with this drag source
3581      * @return {Object} data An object containing arbitrary data
3582      */
3583     getDragData : function(e){
3584         return this.dragData;
3585     },
3586
3587     // private
3588     onDragEnter : function(e, id){
3589         var target = Roo.dd.DragDropMgr.getDDById(id);
3590         this.cachedTarget = target;
3591         if(this.beforeDragEnter(target, e, id) !== false){
3592             if(target.isNotifyTarget){
3593                 var status = target.notifyEnter(this, e, this.dragData);
3594                 this.proxy.setStatus(status);
3595             }else{
3596                 this.proxy.setStatus(this.dropAllowed);
3597             }
3598             
3599             if(this.afterDragEnter){
3600                 /**
3601                  * An empty function by default, but provided so that you can perform a custom action
3602                  * when the dragged item enters the drop target by providing an implementation.
3603                  * @param {Roo.dd.DragDrop} target The drop target
3604                  * @param {Event} e The event object
3605                  * @param {String} id The id of the dragged element
3606                  * @method afterDragEnter
3607                  */
3608                 this.afterDragEnter(target, e, id);
3609             }
3610         }
3611     },
3612
3613     /**
3614      * An empty function by default, but provided so that you can perform a custom action
3615      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3616      * @param {Roo.dd.DragDrop} target The drop target
3617      * @param {Event} e The event object
3618      * @param {String} id The id of the dragged element
3619      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3620      */
3621     beforeDragEnter : function(target, e, id){
3622         return true;
3623     },
3624
3625     // private
3626     alignElWithMouse: function() {
3627         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3628         this.proxy.sync();
3629     },
3630
3631     // private
3632     onDragOver : function(e, id){
3633         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3634         if(this.beforeDragOver(target, e, id) !== false){
3635             if(target.isNotifyTarget){
3636                 var status = target.notifyOver(this, e, this.dragData);
3637                 this.proxy.setStatus(status);
3638             }
3639
3640             if(this.afterDragOver){
3641                 /**
3642                  * An empty function by default, but provided so that you can perform a custom action
3643                  * while the dragged item is over the drop target by providing an implementation.
3644                  * @param {Roo.dd.DragDrop} target The drop target
3645                  * @param {Event} e The event object
3646                  * @param {String} id The id of the dragged element
3647                  * @method afterDragOver
3648                  */
3649                 this.afterDragOver(target, e, id);
3650             }
3651         }
3652     },
3653
3654     /**
3655      * An empty function by default, but provided so that you can perform a custom action
3656      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3657      * @param {Roo.dd.DragDrop} target The drop target
3658      * @param {Event} e The event object
3659      * @param {String} id The id of the dragged element
3660      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3661      */
3662     beforeDragOver : function(target, e, id){
3663         return true;
3664     },
3665
3666     // private
3667     onDragOut : function(e, id){
3668         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3669         if(this.beforeDragOut(target, e, id) !== false){
3670             if(target.isNotifyTarget){
3671                 target.notifyOut(this, e, this.dragData);
3672             }
3673             this.proxy.reset();
3674             if(this.afterDragOut){
3675                 /**
3676                  * An empty function by default, but provided so that you can perform a custom action
3677                  * after the dragged item is dragged out of the target without dropping.
3678                  * @param {Roo.dd.DragDrop} target The drop target
3679                  * @param {Event} e The event object
3680                  * @param {String} id The id of the dragged element
3681                  * @method afterDragOut
3682                  */
3683                 this.afterDragOut(target, e, id);
3684             }
3685         }
3686         this.cachedTarget = null;
3687     },
3688
3689     /**
3690      * An empty function by default, but provided so that you can perform a custom action before the dragged
3691      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3692      * @param {Roo.dd.DragDrop} target The drop target
3693      * @param {Event} e The event object
3694      * @param {String} id The id of the dragged element
3695      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3696      */
3697     beforeDragOut : function(target, e, id){
3698         return true;
3699     },
3700     
3701     // private
3702     onDragDrop : function(e, id){
3703         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3704         if(this.beforeDragDrop(target, e, id) !== false){
3705             if(target.isNotifyTarget){
3706                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3707                     this.onValidDrop(target, e, id);
3708                 }else{
3709                     this.onInvalidDrop(target, e, id);
3710                 }
3711             }else{
3712                 this.onValidDrop(target, e, id);
3713             }
3714             
3715             if(this.afterDragDrop){
3716                 /**
3717                  * An empty function by default, but provided so that you can perform a custom action
3718                  * after a valid drag drop has occurred by providing an implementation.
3719                  * @param {Roo.dd.DragDrop} target The drop target
3720                  * @param {Event} e The event object
3721                  * @param {String} id The id of the dropped element
3722                  * @method afterDragDrop
3723                  */
3724                 this.afterDragDrop(target, e, id);
3725             }
3726         }
3727         delete this.cachedTarget;
3728     },
3729
3730     /**
3731      * An empty function by default, but provided so that you can perform a custom action before the dragged
3732      * item is dropped onto the target and optionally cancel the onDragDrop.
3733      * @param {Roo.dd.DragDrop} target The drop target
3734      * @param {Event} e The event object
3735      * @param {String} id The id of the dragged element
3736      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3737      */
3738     beforeDragDrop : function(target, e, id){
3739         return true;
3740     },
3741
3742     // private
3743     onValidDrop : function(target, e, id){
3744         this.hideProxy();
3745         if(this.afterValidDrop){
3746             /**
3747              * An empty function by default, but provided so that you can perform a custom action
3748              * after a valid drop has occurred by providing an implementation.
3749              * @param {Object} target The target DD 
3750              * @param {Event} e The event object
3751              * @param {String} id The id of the dropped element
3752              * @method afterInvalidDrop
3753              */
3754             this.afterValidDrop(target, e, id);
3755         }
3756     },
3757
3758     // private
3759     getRepairXY : function(e, data){
3760         return this.el.getXY();  
3761     },
3762
3763     // private
3764     onInvalidDrop : function(target, e, id){
3765         this.beforeInvalidDrop(target, e, id);
3766         if(this.cachedTarget){
3767             if(this.cachedTarget.isNotifyTarget){
3768                 this.cachedTarget.notifyOut(this, e, this.dragData);
3769             }
3770             this.cacheTarget = null;
3771         }
3772         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3773
3774         if(this.afterInvalidDrop){
3775             /**
3776              * An empty function by default, but provided so that you can perform a custom action
3777              * after an invalid drop has occurred by providing an implementation.
3778              * @param {Event} e The event object
3779              * @param {String} id The id of the dropped element
3780              * @method afterInvalidDrop
3781              */
3782             this.afterInvalidDrop(e, id);
3783         }
3784     },
3785
3786     // private
3787     afterRepair : function(){
3788         if(Roo.enableFx){
3789             this.el.highlight(this.hlColor || "c3daf9");
3790         }
3791         this.dragging = false;
3792     },
3793
3794     /**
3795      * An empty function by default, but provided so that you can perform a custom action after an invalid
3796      * drop has occurred.
3797      * @param {Roo.dd.DragDrop} target The drop target
3798      * @param {Event} e The event object
3799      * @param {String} id The id of the dragged element
3800      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3801      */
3802     beforeInvalidDrop : function(target, e, id){
3803         return true;
3804     },
3805
3806     // private
3807     handleMouseDown : function(e){
3808         if(this.dragging) {
3809             return;
3810         }
3811         var data = this.getDragData(e);
3812         if(data && this.onBeforeDrag(data, e) !== false){
3813             this.dragData = data;
3814             this.proxy.stop();
3815             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3816         } 
3817     },
3818
3819     /**
3820      * An empty function by default, but provided so that you can perform a custom action before the initial
3821      * drag event begins and optionally cancel it.
3822      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3823      * @param {Event} e The event object
3824      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3825      */
3826     onBeforeDrag : function(data, e){
3827         return true;
3828     },
3829
3830     /**
3831      * An empty function by default, but provided so that you can perform a custom action once the initial
3832      * drag event has begun.  The drag cannot be canceled from this function.
3833      * @param {Number} x The x position of the click on the dragged object
3834      * @param {Number} y The y position of the click on the dragged object
3835      */
3836     onStartDrag : Roo.emptyFn,
3837
3838     // private - YUI override
3839     startDrag : function(x, y){
3840         this.proxy.reset();
3841         this.dragging = true;
3842         this.proxy.update("");
3843         this.onInitDrag(x, y);
3844         this.proxy.show();
3845     },
3846
3847     // private
3848     onInitDrag : function(x, y){
3849         var clone = this.el.dom.cloneNode(true);
3850         clone.id = Roo.id(); // prevent duplicate ids
3851         this.proxy.update(clone);
3852         this.onStartDrag(x, y);
3853         return true;
3854     },
3855
3856     /**
3857      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3858      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3859      */
3860     getProxy : function(){
3861         return this.proxy;  
3862     },
3863
3864     /**
3865      * Hides the drag source's {@link Roo.dd.StatusProxy}
3866      */
3867     hideProxy : function(){
3868         this.proxy.hide();  
3869         this.proxy.reset(true);
3870         this.dragging = false;
3871     },
3872
3873     // private
3874     triggerCacheRefresh : function(){
3875         Roo.dd.DDM.refreshCache(this.groups);
3876     },
3877
3878     // private - override to prevent hiding
3879     b4EndDrag: function(e) {
3880     },
3881
3882     // private - override to prevent moving
3883     endDrag : function(e){
3884         this.onEndDrag(this.dragData, e);
3885     },
3886
3887     // private
3888     onEndDrag : function(data, e){
3889     },
3890     
3891     // private - pin to cursor
3892     autoOffset : function(x, y) {
3893         this.setDelta(-12, -20);
3894     }    
3895 });/*
3896  * Based on:
3897  * Ext JS Library 1.1.1
3898  * Copyright(c) 2006-2007, Ext JS, LLC.
3899  *
3900  * Originally Released Under LGPL - original licence link has changed is not relivant.
3901  *
3902  * Fork - LGPL
3903  * <script type="text/javascript">
3904  */
3905
3906
3907 /**
3908  * @class Roo.dd.DropTarget
3909  * @extends Roo.dd.DDTarget
3910  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3911  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3912  * @constructor
3913  * @param {String/HTMLElement/Element} el The container element
3914  * @param {Object} config
3915  */
3916 Roo.dd.DropTarget = function(el, config){
3917     this.el = Roo.get(el);
3918     
3919     var listeners = false; ;
3920     if (config && config.listeners) {
3921         listeners= config.listeners;
3922         delete config.listeners;
3923     }
3924     Roo.apply(this, config);
3925     
3926     if(this.containerScroll){
3927         Roo.dd.ScrollManager.register(this.el);
3928     }
3929     this.addEvents( {
3930          /**
3931          * @scope Roo.dd.DropTarget
3932          */
3933          
3934          /**
3935          * @event enter
3936          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3937          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3938          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3939          * 
3940          * IMPORTANT : it should set this.overClass and this.dropAllowed
3941          * 
3942          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3943          * @param {Event} e The event
3944          * @param {Object} data An object containing arbitrary data supplied by the drag source
3945          */
3946         "enter" : true,
3947         
3948          /**
3949          * @event over
3950          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3951          * This method will be called on every mouse movement while the drag source is over the drop target.
3952          * This default implementation simply returns the dropAllowed config value.
3953          * 
3954          * IMPORTANT : it should set this.dropAllowed
3955          * 
3956          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3957          * @param {Event} e The event
3958          * @param {Object} data An object containing arbitrary data supplied by the drag source
3959          
3960          */
3961         "over" : true,
3962         /**
3963          * @event out
3964          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3965          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3966          * overClass (if any) from the drop element.
3967          * 
3968          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3969          * @param {Event} e The event
3970          * @param {Object} data An object containing arbitrary data supplied by the drag source
3971          */
3972          "out" : true,
3973          
3974         /**
3975          * @event drop
3976          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3977          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3978          * implementation that does something to process the drop event and returns true so that the drag source's
3979          * repair action does not run.
3980          * 
3981          * IMPORTANT : it should set this.success
3982          * 
3983          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3984          * @param {Event} e The event
3985          * @param {Object} data An object containing arbitrary data supplied by the drag source
3986         */
3987          "drop" : true
3988     });
3989             
3990      
3991     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3992         this.el.dom, 
3993         this.ddGroup || this.group,
3994         {
3995             isTarget: true,
3996             listeners : listeners || {} 
3997            
3998         
3999         }
4000     );
4001
4002 };
4003
4004 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
4005     /**
4006      * @cfg {String} overClass
4007      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
4008      */
4009      /**
4010      * @cfg {String} ddGroup
4011      * The drag drop group to handle drop events for
4012      */
4013      
4014     /**
4015      * @cfg {String} dropAllowed
4016      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
4017      */
4018     dropAllowed : "x-dd-drop-ok",
4019     /**
4020      * @cfg {String} dropNotAllowed
4021      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
4022      */
4023     dropNotAllowed : "x-dd-drop-nodrop",
4024     /**
4025      * @cfg {boolean} success
4026      * set this after drop listener.. 
4027      */
4028     success : false,
4029     /**
4030      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4031      * if the drop point is valid for over/enter..
4032      */
4033     valid : false,
4034     // private
4035     isTarget : true,
4036
4037     // private
4038     isNotifyTarget : true,
4039     
4040     /**
4041      * @hide
4042      */
4043     notifyEnter : function(dd, e, data)
4044     {
4045         this.valid = true;
4046         this.fireEvent('enter', dd, e, data);
4047         if(this.overClass){
4048             this.el.addClass(this.overClass);
4049         }
4050         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4051             this.valid ? this.dropAllowed : this.dropNotAllowed
4052         );
4053     },
4054
4055     /**
4056      * @hide
4057      */
4058     notifyOver : function(dd, e, data)
4059     {
4060         this.valid = true;
4061         this.fireEvent('over', dd, e, data);
4062         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4063             this.valid ? this.dropAllowed : this.dropNotAllowed
4064         );
4065     },
4066
4067     /**
4068      * @hide
4069      */
4070     notifyOut : function(dd, e, data)
4071     {
4072         this.fireEvent('out', dd, e, data);
4073         if(this.overClass){
4074             this.el.removeClass(this.overClass);
4075         }
4076     },
4077
4078     /**
4079      * @hide
4080      */
4081     notifyDrop : function(dd, e, data)
4082     {
4083         this.success = false;
4084         this.fireEvent('drop', dd, e, data);
4085         return this.success;
4086     }
4087 });/*
4088  * Based on:
4089  * Ext JS Library 1.1.1
4090  * Copyright(c) 2006-2007, Ext JS, LLC.
4091  *
4092  * Originally Released Under LGPL - original licence link has changed is not relivant.
4093  *
4094  * Fork - LGPL
4095  * <script type="text/javascript">
4096  */
4097
4098
4099 /**
4100  * @class Roo.dd.DragZone
4101  * @extends Roo.dd.DragSource
4102  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4103  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4104  * @constructor
4105  * @param {String/HTMLElement/Element} el The container element
4106  * @param {Object} config
4107  */
4108 Roo.dd.DragZone = function(el, config){
4109     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4110     if(this.containerScroll){
4111         Roo.dd.ScrollManager.register(this.el);
4112     }
4113 };
4114
4115 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4116     /**
4117      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4118      * for auto scrolling during drag operations.
4119      */
4120     /**
4121      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4122      * method after a failed drop (defaults to "c3daf9" - light blue)
4123      */
4124
4125     /**
4126      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4127      * for a valid target to drag based on the mouse down. Override this method
4128      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4129      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4130      * @param {EventObject} e The mouse down event
4131      * @return {Object} The dragData
4132      */
4133     getDragData : function(e){
4134         return Roo.dd.Registry.getHandleFromEvent(e);
4135     },
4136     
4137     /**
4138      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4139      * this.dragData.ddel
4140      * @param {Number} x The x position of the click on the dragged object
4141      * @param {Number} y The y position of the click on the dragged object
4142      * @return {Boolean} true to continue the drag, false to cancel
4143      */
4144     onInitDrag : function(x, y){
4145         this.proxy.update(this.dragData.ddel.cloneNode(true));
4146         this.onStartDrag(x, y);
4147         return true;
4148     },
4149     
4150     /**
4151      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4152      */
4153     afterRepair : function(){
4154         if(Roo.enableFx){
4155             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4156         }
4157         this.dragging = false;
4158     },
4159
4160     /**
4161      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4162      * the XY of this.dragData.ddel
4163      * @param {EventObject} e The mouse up event
4164      * @return {Array} The xy location (e.g. [100, 200])
4165      */
4166     getRepairXY : function(e){
4167         return Roo.Element.fly(this.dragData.ddel).getXY();  
4168     }
4169 });/*
4170  * Based on:
4171  * Ext JS Library 1.1.1
4172  * Copyright(c) 2006-2007, Ext JS, LLC.
4173  *
4174  * Originally Released Under LGPL - original licence link has changed is not relivant.
4175  *
4176  * Fork - LGPL
4177  * <script type="text/javascript">
4178  */
4179 /**
4180  * @class Roo.dd.DropZone
4181  * @extends Roo.dd.DropTarget
4182  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4183  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4184  * @constructor
4185  * @param {String/HTMLElement/Element} el The container element
4186  * @param {Object} config
4187  */
4188 Roo.dd.DropZone = function(el, config){
4189     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4190 };
4191
4192 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4193     /**
4194      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4195      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4196      * provide your own custom lookup.
4197      * @param {Event} e The event
4198      * @return {Object} data The custom data
4199      */
4200     getTargetFromEvent : function(e){
4201         return Roo.dd.Registry.getTargetFromEvent(e);
4202     },
4203
4204     /**
4205      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4206      * that it has registered.  This method has no default implementation and should be overridden to provide
4207      * node-specific processing if necessary.
4208      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4209      * {@link #getTargetFromEvent} for this node)
4210      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4211      * @param {Event} e The event
4212      * @param {Object} data An object containing arbitrary data supplied by the drag source
4213      */
4214     onNodeEnter : function(n, dd, e, data){
4215         
4216     },
4217
4218     /**
4219      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4220      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4221      * overridden to provide the proper feedback.
4222      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4223      * {@link #getTargetFromEvent} for this node)
4224      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4225      * @param {Event} e The event
4226      * @param {Object} data An object containing arbitrary data supplied by the drag source
4227      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4228      * underlying {@link Roo.dd.StatusProxy} can be updated
4229      */
4230     onNodeOver : function(n, dd, e, data){
4231         return this.dropAllowed;
4232     },
4233
4234     /**
4235      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4236      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4237      * node-specific processing if necessary.
4238      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4239      * {@link #getTargetFromEvent} for this node)
4240      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4241      * @param {Event} e The event
4242      * @param {Object} data An object containing arbitrary data supplied by the drag source
4243      */
4244     onNodeOut : function(n, dd, e, data){
4245         
4246     },
4247
4248     /**
4249      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4250      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4251      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4252      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4253      * {@link #getTargetFromEvent} for this node)
4254      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4255      * @param {Event} e The event
4256      * @param {Object} data An object containing arbitrary data supplied by the drag source
4257      * @return {Boolean} True if the drop was valid, else false
4258      */
4259     onNodeDrop : function(n, dd, e, data){
4260         return false;
4261     },
4262
4263     /**
4264      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4265      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4266      * it should be overridden to provide the proper feedback if necessary.
4267      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4268      * @param {Event} e The event
4269      * @param {Object} data An object containing arbitrary data supplied by the drag source
4270      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4271      * underlying {@link Roo.dd.StatusProxy} can be updated
4272      */
4273     onContainerOver : function(dd, e, data){
4274         return this.dropNotAllowed;
4275     },
4276
4277     /**
4278      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4279      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4280      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4281      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4282      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4283      * @param {Event} e The event
4284      * @param {Object} data An object containing arbitrary data supplied by the drag source
4285      * @return {Boolean} True if the drop was valid, else false
4286      */
4287     onContainerDrop : function(dd, e, data){
4288         return false;
4289     },
4290
4291     /**
4292      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4293      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4294      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4295      * you should override this method and provide a custom implementation.
4296      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4297      * @param {Event} e The event
4298      * @param {Object} data An object containing arbitrary data supplied by the drag source
4299      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4300      * underlying {@link Roo.dd.StatusProxy} can be updated
4301      */
4302     notifyEnter : function(dd, e, data){
4303         return this.dropNotAllowed;
4304     },
4305
4306     /**
4307      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4308      * This method will be called on every mouse movement while the drag source is over the drop zone.
4309      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4310      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4311      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4312      * registered node, it will call {@link #onContainerOver}.
4313      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4314      * @param {Event} e The event
4315      * @param {Object} data An object containing arbitrary data supplied by the drag source
4316      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4317      * underlying {@link Roo.dd.StatusProxy} can be updated
4318      */
4319     notifyOver : function(dd, e, data){
4320         var n = this.getTargetFromEvent(e);
4321         if(!n){ // not over valid drop target
4322             if(this.lastOverNode){
4323                 this.onNodeOut(this.lastOverNode, dd, e, data);
4324                 this.lastOverNode = null;
4325             }
4326             return this.onContainerOver(dd, e, data);
4327         }
4328         if(this.lastOverNode != n){
4329             if(this.lastOverNode){
4330                 this.onNodeOut(this.lastOverNode, dd, e, data);
4331             }
4332             this.onNodeEnter(n, dd, e, data);
4333             this.lastOverNode = n;
4334         }
4335         return this.onNodeOver(n, dd, e, data);
4336     },
4337
4338     /**
4339      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4340      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4341      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4342      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4343      * @param {Event} e The event
4344      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4345      */
4346     notifyOut : function(dd, e, data){
4347         if(this.lastOverNode){
4348             this.onNodeOut(this.lastOverNode, dd, e, data);
4349             this.lastOverNode = null;
4350         }
4351     },
4352
4353     /**
4354      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4355      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4356      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4357      * otherwise it will call {@link #onContainerDrop}.
4358      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4359      * @param {Event} e The event
4360      * @param {Object} data An object containing arbitrary data supplied by the drag source
4361      * @return {Boolean} True if the drop was valid, else false
4362      */
4363     notifyDrop : function(dd, e, data){
4364         if(this.lastOverNode){
4365             this.onNodeOut(this.lastOverNode, dd, e, data);
4366             this.lastOverNode = null;
4367         }
4368         var n = this.getTargetFromEvent(e);
4369         return n ?
4370             this.onNodeDrop(n, dd, e, data) :
4371             this.onContainerDrop(dd, e, data);
4372     },
4373
4374     // private
4375     triggerCacheRefresh : function(){
4376         Roo.dd.DDM.refreshCache(this.groups);
4377     }  
4378 });/*
4379  * Based on:
4380  * Ext JS Library 1.1.1
4381  * Copyright(c) 2006-2007, Ext JS, LLC.
4382  *
4383  * Originally Released Under LGPL - original licence link has changed is not relivant.
4384  *
4385  * Fork - LGPL
4386  * <script type="text/javascript">
4387  */
4388
4389
4390 /**
4391  * @class Roo.data.SortTypes
4392  * @singleton
4393  * Defines the default sorting (casting?) comparison functions used when sorting data.
4394  */
4395 Roo.data.SortTypes = {
4396     /**
4397      * Default sort that does nothing
4398      * @param {Mixed} s The value being converted
4399      * @return {Mixed} The comparison value
4400      */
4401     none : function(s){
4402         return s;
4403     },
4404     
4405     /**
4406      * The regular expression used to strip tags
4407      * @type {RegExp}
4408      * @property
4409      */
4410     stripTagsRE : /<\/?[^>]+>/gi,
4411     
4412     /**
4413      * Strips all HTML tags to sort on text only
4414      * @param {Mixed} s The value being converted
4415      * @return {String} The comparison value
4416      */
4417     asText : function(s){
4418         return String(s).replace(this.stripTagsRE, "");
4419     },
4420     
4421     /**
4422      * Strips all HTML tags to sort on text only - Case insensitive
4423      * @param {Mixed} s The value being converted
4424      * @return {String} The comparison value
4425      */
4426     asUCText : function(s){
4427         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4428     },
4429     
4430     /**
4431      * Case insensitive string
4432      * @param {Mixed} s The value being converted
4433      * @return {String} The comparison value
4434      */
4435     asUCString : function(s) {
4436         return String(s).toUpperCase();
4437     },
4438     
4439     /**
4440      * Date sorting
4441      * @param {Mixed} s The value being converted
4442      * @return {Number} The comparison value
4443      */
4444     asDate : function(s) {
4445         if(!s){
4446             return 0;
4447         }
4448         if(s instanceof Date){
4449             return s.getTime();
4450         }
4451         return Date.parse(String(s));
4452     },
4453     
4454     /**
4455      * Float sorting
4456      * @param {Mixed} s The value being converted
4457      * @return {Float} The comparison value
4458      */
4459     asFloat : function(s) {
4460         var val = parseFloat(String(s).replace(/,/g, ""));
4461         if(isNaN(val)) val = 0;
4462         return val;
4463     },
4464     
4465     /**
4466      * Integer sorting
4467      * @param {Mixed} s The value being converted
4468      * @return {Number} The comparison value
4469      */
4470     asInt : function(s) {
4471         var val = parseInt(String(s).replace(/,/g, ""));
4472         if(isNaN(val)) val = 0;
4473         return val;
4474     }
4475 };/*
4476  * Based on:
4477  * Ext JS Library 1.1.1
4478  * Copyright(c) 2006-2007, Ext JS, LLC.
4479  *
4480  * Originally Released Under LGPL - original licence link has changed is not relivant.
4481  *
4482  * Fork - LGPL
4483  * <script type="text/javascript">
4484  */
4485
4486 /**
4487 * @class Roo.data.Record
4488  * Instances of this class encapsulate both record <em>definition</em> information, and record
4489  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4490  * to access Records cached in an {@link Roo.data.Store} object.<br>
4491  * <p>
4492  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4493  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4494  * objects.<br>
4495  * <p>
4496  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4497  * @constructor
4498  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4499  * {@link #create}. The parameters are the same.
4500  * @param {Array} data An associative Array of data values keyed by the field name.
4501  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4502  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4503  * not specified an integer id is generated.
4504  */
4505 Roo.data.Record = function(data, id){
4506     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4507     this.data = data;
4508 };
4509
4510 /**
4511  * Generate a constructor for a specific record layout.
4512  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4513  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4514  * Each field definition object may contain the following properties: <ul>
4515  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
4516  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4517  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4518  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4519  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4520  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4521  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4522  * this may be omitted.</p></li>
4523  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4524  * <ul><li>auto (Default, implies no conversion)</li>
4525  * <li>string</li>
4526  * <li>int</li>
4527  * <li>float</li>
4528  * <li>boolean</li>
4529  * <li>date</li></ul></p></li>
4530  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4531  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4532  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4533  * by the Reader into an object that will be stored in the Record. It is passed the
4534  * following parameters:<ul>
4535  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4536  * </ul></p></li>
4537  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4538  * </ul>
4539  * <br>usage:<br><pre><code>
4540 var TopicRecord = Roo.data.Record.create(
4541     {name: 'title', mapping: 'topic_title'},
4542     {name: 'author', mapping: 'username'},
4543     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4544     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4545     {name: 'lastPoster', mapping: 'user2'},
4546     {name: 'excerpt', mapping: 'post_text'}
4547 );
4548
4549 var myNewRecord = new TopicRecord({
4550     title: 'Do my job please',
4551     author: 'noobie',
4552     totalPosts: 1,
4553     lastPost: new Date(),
4554     lastPoster: 'Animal',
4555     excerpt: 'No way dude!'
4556 });
4557 myStore.add(myNewRecord);
4558 </code></pre>
4559  * @method create
4560  * @static
4561  */
4562 Roo.data.Record.create = function(o){
4563     var f = function(){
4564         f.superclass.constructor.apply(this, arguments);
4565     };
4566     Roo.extend(f, Roo.data.Record);
4567     var p = f.prototype;
4568     p.fields = new Roo.util.MixedCollection(false, function(field){
4569         return field.name;
4570     });
4571     for(var i = 0, len = o.length; i < len; i++){
4572         p.fields.add(new Roo.data.Field(o[i]));
4573     }
4574     f.getField = function(name){
4575         return p.fields.get(name);  
4576     };
4577     return f;
4578 };
4579
4580 Roo.data.Record.AUTO_ID = 1000;
4581 Roo.data.Record.EDIT = 'edit';
4582 Roo.data.Record.REJECT = 'reject';
4583 Roo.data.Record.COMMIT = 'commit';
4584
4585 Roo.data.Record.prototype = {
4586     /**
4587      * Readonly flag - true if this record has been modified.
4588      * @type Boolean
4589      */
4590     dirty : false,
4591     editing : false,
4592     error: null,
4593     modified: null,
4594
4595     // private
4596     join : function(store){
4597         this.store = store;
4598     },
4599
4600     /**
4601      * Set the named field to the specified value.
4602      * @param {String} name The name of the field to set.
4603      * @param {Object} value The value to set the field to.
4604      */
4605     set : function(name, value){
4606         if(this.data[name] == value){
4607             return;
4608         }
4609         this.dirty = true;
4610         if(!this.modified){
4611             this.modified = {};
4612         }
4613         if(typeof this.modified[name] == 'undefined'){
4614             this.modified[name] = this.data[name];
4615         }
4616         this.data[name] = value;
4617         if(!this.editing && this.store){
4618             this.store.afterEdit(this);
4619         }       
4620     },
4621
4622     /**
4623      * Get the value of the named field.
4624      * @param {String} name The name of the field to get the value of.
4625      * @return {Object} The value of the field.
4626      */
4627     get : function(name){
4628         return this.data[name]; 
4629     },
4630
4631     // private
4632     beginEdit : function(){
4633         this.editing = true;
4634         this.modified = {}; 
4635     },
4636
4637     // private
4638     cancelEdit : function(){
4639         this.editing = false;
4640         delete this.modified;
4641     },
4642
4643     // private
4644     endEdit : function(){
4645         this.editing = false;
4646         if(this.dirty && this.store){
4647             this.store.afterEdit(this);
4648         }
4649     },
4650
4651     /**
4652      * Usually called by the {@link Roo.data.Store} which owns the Record.
4653      * Rejects all changes made to the Record since either creation, or the last commit operation.
4654      * Modified fields are reverted to their original values.
4655      * <p>
4656      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4657      * of reject operations.
4658      */
4659     reject : function(){
4660         var m = this.modified;
4661         for(var n in m){
4662             if(typeof m[n] != "function"){
4663                 this.data[n] = m[n];
4664             }
4665         }
4666         this.dirty = false;
4667         delete this.modified;
4668         this.editing = false;
4669         if(this.store){
4670             this.store.afterReject(this);
4671         }
4672     },
4673
4674     /**
4675      * Usually called by the {@link Roo.data.Store} which owns the Record.
4676      * Commits all changes made to the Record since either creation, or the last commit operation.
4677      * <p>
4678      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4679      * of commit operations.
4680      */
4681     commit : function(){
4682         this.dirty = false;
4683         delete this.modified;
4684         this.editing = false;
4685         if(this.store){
4686             this.store.afterCommit(this);
4687         }
4688     },
4689
4690     // private
4691     hasError : function(){
4692         return this.error != null;
4693     },
4694
4695     // private
4696     clearError : function(){
4697         this.error = null;
4698     },
4699
4700     /**
4701      * Creates a copy of this record.
4702      * @param {String} id (optional) A new record id if you don't want to use this record's id
4703      * @return {Record}
4704      */
4705     copy : function(newId) {
4706         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4707     }
4708 };/*
4709  * Based on:
4710  * Ext JS Library 1.1.1
4711  * Copyright(c) 2006-2007, Ext JS, LLC.
4712  *
4713  * Originally Released Under LGPL - original licence link has changed is not relivant.
4714  *
4715  * Fork - LGPL
4716  * <script type="text/javascript">
4717  */
4718
4719
4720
4721 /**
4722  * @class Roo.data.Store
4723  * @extends Roo.util.Observable
4724  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4725  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4726  * <p>
4727  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
4728  * has no knowledge of the format of the data returned by the Proxy.<br>
4729  * <p>
4730  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4731  * instances from the data object. These records are cached and made available through accessor functions.
4732  * @constructor
4733  * Creates a new Store.
4734  * @param {Object} config A config object containing the objects needed for the Store to access data,
4735  * and read the data into Records.
4736  */
4737 Roo.data.Store = function(config){
4738     this.data = new Roo.util.MixedCollection(false);
4739     this.data.getKey = function(o){
4740         return o.id;
4741     };
4742     this.baseParams = {};
4743     // private
4744     this.paramNames = {
4745         "start" : "start",
4746         "limit" : "limit",
4747         "sort" : "sort",
4748         "dir" : "dir",
4749         "multisort" : "_multisort"
4750     };
4751
4752     if(config && config.data){
4753         this.inlineData = config.data;
4754         delete config.data;
4755     }
4756
4757     Roo.apply(this, config);
4758     
4759     if(this.reader){ // reader passed
4760         this.reader = Roo.factory(this.reader, Roo.data);
4761         this.reader.xmodule = this.xmodule || false;
4762         if(!this.recordType){
4763             this.recordType = this.reader.recordType;
4764         }
4765         if(this.reader.onMetaChange){
4766             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4767         }
4768     }
4769
4770     if(this.recordType){
4771         this.fields = this.recordType.prototype.fields;
4772     }
4773     this.modified = [];
4774
4775     this.addEvents({
4776         /**
4777          * @event datachanged
4778          * Fires when the data cache has changed, and a widget which is using this Store
4779          * as a Record cache should refresh its view.
4780          * @param {Store} this
4781          */
4782         datachanged : true,
4783         /**
4784          * @event metachange
4785          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4786          * @param {Store} this
4787          * @param {Object} meta The JSON metadata
4788          */
4789         metachange : true,
4790         /**
4791          * @event add
4792          * Fires when Records have been added to the Store
4793          * @param {Store} this
4794          * @param {Roo.data.Record[]} records The array of Records added
4795          * @param {Number} index The index at which the record(s) were added
4796          */
4797         add : true,
4798         /**
4799          * @event remove
4800          * Fires when a Record has been removed from the Store
4801          * @param {Store} this
4802          * @param {Roo.data.Record} record The Record that was removed
4803          * @param {Number} index The index at which the record was removed
4804          */
4805         remove : true,
4806         /**
4807          * @event update
4808          * Fires when a Record has been updated
4809          * @param {Store} this
4810          * @param {Roo.data.Record} record The Record that was updated
4811          * @param {String} operation The update operation being performed.  Value may be one of:
4812          * <pre><code>
4813  Roo.data.Record.EDIT
4814  Roo.data.Record.REJECT
4815  Roo.data.Record.COMMIT
4816          * </code></pre>
4817          */
4818         update : true,
4819         /**
4820          * @event clear
4821          * Fires when the data cache has been cleared.
4822          * @param {Store} this
4823          */
4824         clear : true,
4825         /**
4826          * @event beforeload
4827          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4828          * the load action will be canceled.
4829          * @param {Store} this
4830          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4831          */
4832         beforeload : true,
4833         /**
4834          * @event beforeloadadd
4835          * Fires after a new set of Records has been loaded.
4836          * @param {Store} this
4837          * @param {Roo.data.Record[]} records The Records that were loaded
4838          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4839          */
4840         beforeloadadd : true,
4841         /**
4842          * @event load
4843          * Fires after a new set of Records has been loaded, before they are added to the store.
4844          * @param {Store} this
4845          * @param {Roo.data.Record[]} records The Records that were loaded
4846          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4847          * @params {Object} return from reader
4848          */
4849         load : true,
4850         /**
4851          * @event loadexception
4852          * Fires if an exception occurs in the Proxy during loading.
4853          * Called with the signature of the Proxy's "loadexception" event.
4854          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4855          * 
4856          * @param {Proxy} 
4857          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4858          * @param {Object} load options 
4859          * @param {Object} jsonData from your request (normally this contains the Exception)
4860          */
4861         loadexception : true
4862     });
4863     
4864     if(this.proxy){
4865         this.proxy = Roo.factory(this.proxy, Roo.data);
4866         this.proxy.xmodule = this.xmodule || false;
4867         this.relayEvents(this.proxy,  ["loadexception"]);
4868     }
4869     this.sortToggle = {};
4870     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4871
4872     Roo.data.Store.superclass.constructor.call(this);
4873
4874     if(this.inlineData){
4875         this.loadData(this.inlineData);
4876         delete this.inlineData;
4877     }
4878 };
4879
4880 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4881      /**
4882     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4883     * without a remote query - used by combo/forms at present.
4884     */
4885     
4886     /**
4887     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4888     */
4889     /**
4890     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4891     */
4892     /**
4893     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4894     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4895     */
4896     /**
4897     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4898     * on any HTTP request
4899     */
4900     /**
4901     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4902     */
4903     /**
4904     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4905     */
4906     multiSort: false,
4907     /**
4908     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4909     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4910     */
4911     remoteSort : false,
4912
4913     /**
4914     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4915      * loaded or when a record is removed. (defaults to false).
4916     */
4917     pruneModifiedRecords : false,
4918
4919     // private
4920     lastOptions : null,
4921
4922     /**
4923      * Add Records to the Store and fires the add event.
4924      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4925      */
4926     add : function(records){
4927         records = [].concat(records);
4928         for(var i = 0, len = records.length; i < len; i++){
4929             records[i].join(this);
4930         }
4931         var index = this.data.length;
4932         this.data.addAll(records);
4933         this.fireEvent("add", this, records, index);
4934     },
4935
4936     /**
4937      * Remove a Record from the Store and fires the remove event.
4938      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4939      */
4940     remove : function(record){
4941         var index = this.data.indexOf(record);
4942         this.data.removeAt(index);
4943         if(this.pruneModifiedRecords){
4944             this.modified.remove(record);
4945         }
4946         this.fireEvent("remove", this, record, index);
4947     },
4948
4949     /**
4950      * Remove all Records from the Store and fires the clear event.
4951      */
4952     removeAll : function(){
4953         this.data.clear();
4954         if(this.pruneModifiedRecords){
4955             this.modified = [];
4956         }
4957         this.fireEvent("clear", this);
4958     },
4959
4960     /**
4961      * Inserts Records to the Store at the given index and fires the add event.
4962      * @param {Number} index The start index at which to insert the passed Records.
4963      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4964      */
4965     insert : function(index, records){
4966         records = [].concat(records);
4967         for(var i = 0, len = records.length; i < len; i++){
4968             this.data.insert(index, records[i]);
4969             records[i].join(this);
4970         }
4971         this.fireEvent("add", this, records, index);
4972     },
4973
4974     /**
4975      * Get the index within the cache of the passed Record.
4976      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4977      * @return {Number} The index of the passed Record. Returns -1 if not found.
4978      */
4979     indexOf : function(record){
4980         return this.data.indexOf(record);
4981     },
4982
4983     /**
4984      * Get the index within the cache of the Record with the passed id.
4985      * @param {String} id The id of the Record to find.
4986      * @return {Number} The index of the Record. Returns -1 if not found.
4987      */
4988     indexOfId : function(id){
4989         return this.data.indexOfKey(id);
4990     },
4991
4992     /**
4993      * Get the Record with the specified id.
4994      * @param {String} id The id of the Record to find.
4995      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4996      */
4997     getById : function(id){
4998         return this.data.key(id);
4999     },
5000
5001     /**
5002      * Get the Record at the specified index.
5003      * @param {Number} index The index of the Record to find.
5004      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
5005      */
5006     getAt : function(index){
5007         return this.data.itemAt(index);
5008     },
5009
5010     /**
5011      * Returns a range of Records between specified indices.
5012      * @param {Number} startIndex (optional) The starting index (defaults to 0)
5013      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
5014      * @return {Roo.data.Record[]} An array of Records
5015      */
5016     getRange : function(start, end){
5017         return this.data.getRange(start, end);
5018     },
5019
5020     // private
5021     storeOptions : function(o){
5022         o = Roo.apply({}, o);
5023         delete o.callback;
5024         delete o.scope;
5025         this.lastOptions = o;
5026     },
5027
5028     /**
5029      * Loads the Record cache from the configured Proxy using the configured Reader.
5030      * <p>
5031      * If using remote paging, then the first load call must specify the <em>start</em>
5032      * and <em>limit</em> properties in the options.params property to establish the initial
5033      * position within the dataset, and the number of Records to cache on each read from the Proxy.
5034      * <p>
5035      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5036      * and this call will return before the new data has been loaded. Perform any post-processing
5037      * in a callback function, or in a "load" event handler.</strong>
5038      * <p>
5039      * @param {Object} options An object containing properties which control loading options:<ul>
5040      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5041      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5042      * passed the following arguments:<ul>
5043      * <li>r : Roo.data.Record[]</li>
5044      * <li>options: Options object from the load call</li>
5045      * <li>success: Boolean success indicator</li></ul></li>
5046      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5047      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5048      * </ul>
5049      */
5050     load : function(options){
5051         options = options || {};
5052         if(this.fireEvent("beforeload", this, options) !== false){
5053             this.storeOptions(options);
5054             var p = Roo.apply(options.params || {}, this.baseParams);
5055             // if meta was not loaded from remote source.. try requesting it.
5056             if (!this.reader.metaFromRemote) {
5057                 p._requestMeta = 1;
5058             }
5059             if(this.sortInfo && this.remoteSort){
5060                 var pn = this.paramNames;
5061                 p[pn["sort"]] = this.sortInfo.field;
5062                 p[pn["dir"]] = this.sortInfo.direction;
5063             }
5064             if (this.multiSort) {
5065                 var pn = this.paramNames;
5066                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5067             }
5068             
5069             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5070         }
5071     },
5072
5073     /**
5074      * Reloads the Record cache from the configured Proxy using the configured Reader and
5075      * the options from the last load operation performed.
5076      * @param {Object} options (optional) An object containing properties which may override the options
5077      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5078      * the most recently used options are reused).
5079      */
5080     reload : function(options){
5081         this.load(Roo.applyIf(options||{}, this.lastOptions));
5082     },
5083
5084     // private
5085     // Called as a callback by the Reader during a load operation.
5086     loadRecords : function(o, options, success){
5087         if(!o || success === false){
5088             if(success !== false){
5089                 this.fireEvent("load", this, [], options, o);
5090             }
5091             if(options.callback){
5092                 options.callback.call(options.scope || this, [], options, false);
5093             }
5094             return;
5095         }
5096         // if data returned failure - throw an exception.
5097         if (o.success === false) {
5098             // show a message if no listener is registered.
5099             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
5100                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
5101             }
5102             // loadmask wil be hooked into this..
5103             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
5104             return;
5105         }
5106         var r = o.records, t = o.totalRecords || r.length;
5107         
5108         this.fireEvent("beforeloadadd", this, r, options, o);
5109         
5110         if(!options || options.add !== true){
5111             if(this.pruneModifiedRecords){
5112                 this.modified = [];
5113             }
5114             for(var i = 0, len = r.length; i < len; i++){
5115                 r[i].join(this);
5116             }
5117             if(this.snapshot){
5118                 this.data = this.snapshot;
5119                 delete this.snapshot;
5120             }
5121             this.data.clear();
5122             this.data.addAll(r);
5123             this.totalLength = t;
5124             this.applySort();
5125             this.fireEvent("datachanged", this);
5126         }else{
5127             this.totalLength = Math.max(t, this.data.length+r.length);
5128             this.add(r);
5129         }
5130         this.fireEvent("load", this, r, options, o);
5131         if(options.callback){
5132             options.callback.call(options.scope || this, r, options, true);
5133         }
5134     },
5135
5136
5137     /**
5138      * Loads data from a passed data block. A Reader which understands the format of the data
5139      * must have been configured in the constructor.
5140      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5141      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5142      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5143      */
5144     loadData : function(o, append){
5145         var r = this.reader.readRecords(o);
5146         this.loadRecords(r, {add: append}, true);
5147     },
5148
5149     /**
5150      * Gets the number of cached records.
5151      * <p>
5152      * <em>If using paging, this may not be the total size of the dataset. If the data object
5153      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5154      * the data set size</em>
5155      */
5156     getCount : function(){
5157         return this.data.length || 0;
5158     },
5159
5160     /**
5161      * Gets the total number of records in the dataset as returned by the server.
5162      * <p>
5163      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5164      * the dataset size</em>
5165      */
5166     getTotalCount : function(){
5167         return this.totalLength || 0;
5168     },
5169
5170     /**
5171      * Returns the sort state of the Store as an object with two properties:
5172      * <pre><code>
5173  field {String} The name of the field by which the Records are sorted
5174  direction {String} The sort order, "ASC" or "DESC"
5175      * </code></pre>
5176      */
5177     getSortState : function(){
5178         return this.sortInfo;
5179     },
5180
5181     // private
5182     applySort : function(){
5183         if(this.sortInfo && !this.remoteSort){
5184             var s = this.sortInfo, f = s.field;
5185             var st = this.fields.get(f).sortType;
5186             var fn = function(r1, r2){
5187                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5188                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5189             };
5190             this.data.sort(s.direction, fn);
5191             if(this.snapshot && this.snapshot != this.data){
5192                 this.snapshot.sort(s.direction, fn);
5193             }
5194         }
5195     },
5196
5197     /**
5198      * Sets the default sort column and order to be used by the next load operation.
5199      * @param {String} fieldName The name of the field to sort by.
5200      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5201      */
5202     setDefaultSort : function(field, dir){
5203         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5204     },
5205
5206     /**
5207      * Sort the Records.
5208      * If remote sorting is used, the sort is performed on the server, and the cache is
5209      * reloaded. If local sorting is used, the cache is sorted internally.
5210      * @param {String} fieldName The name of the field to sort by.
5211      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5212      */
5213     sort : function(fieldName, dir){
5214         var f = this.fields.get(fieldName);
5215         if(!dir){
5216             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5217             
5218             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5219                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5220             }else{
5221                 dir = f.sortDir;
5222             }
5223         }
5224         this.sortToggle[f.name] = dir;
5225         this.sortInfo = {field: f.name, direction: dir};
5226         if(!this.remoteSort){
5227             this.applySort();
5228             this.fireEvent("datachanged", this);
5229         }else{
5230             this.load(this.lastOptions);
5231         }
5232     },
5233
5234     /**
5235      * Calls the specified function for each of the Records in the cache.
5236      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5237      * Returning <em>false</em> aborts and exits the iteration.
5238      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5239      */
5240     each : function(fn, scope){
5241         this.data.each(fn, scope);
5242     },
5243
5244     /**
5245      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5246      * (e.g., during paging).
5247      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5248      */
5249     getModifiedRecords : function(){
5250         return this.modified;
5251     },
5252
5253     // private
5254     createFilterFn : function(property, value, anyMatch){
5255         if(!value.exec){ // not a regex
5256             value = String(value);
5257             if(value.length == 0){
5258                 return false;
5259             }
5260             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5261         }
5262         return function(r){
5263             return value.test(r.data[property]);
5264         };
5265     },
5266
5267     /**
5268      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5269      * @param {String} property A field on your records
5270      * @param {Number} start The record index to start at (defaults to 0)
5271      * @param {Number} end The last record index to include (defaults to length - 1)
5272      * @return {Number} The sum
5273      */
5274     sum : function(property, start, end){
5275         var rs = this.data.items, v = 0;
5276         start = start || 0;
5277         end = (end || end === 0) ? end : rs.length-1;
5278
5279         for(var i = start; i <= end; i++){
5280             v += (rs[i].data[property] || 0);
5281         }
5282         return v;
5283     },
5284
5285     /**
5286      * Filter the records by a specified property.
5287      * @param {String} field A field on your records
5288      * @param {String/RegExp} value Either a string that the field
5289      * should start with or a RegExp to test against the field
5290      * @param {Boolean} anyMatch True to match any part not just the beginning
5291      */
5292     filter : function(property, value, anyMatch){
5293         var fn = this.createFilterFn(property, value, anyMatch);
5294         return fn ? this.filterBy(fn) : this.clearFilter();
5295     },
5296
5297     /**
5298      * Filter by a function. The specified function will be called with each
5299      * record in this data source. If the function returns true the record is included,
5300      * otherwise it is filtered.
5301      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5302      * @param {Object} scope (optional) The scope of the function (defaults to this)
5303      */
5304     filterBy : function(fn, scope){
5305         this.snapshot = this.snapshot || this.data;
5306         this.data = this.queryBy(fn, scope||this);
5307         this.fireEvent("datachanged", this);
5308     },
5309
5310     /**
5311      * Query the records by a specified property.
5312      * @param {String} field A field on your records
5313      * @param {String/RegExp} value Either a string that the field
5314      * should start with or a RegExp to test against the field
5315      * @param {Boolean} anyMatch True to match any part not just the beginning
5316      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5317      */
5318     query : function(property, value, anyMatch){
5319         var fn = this.createFilterFn(property, value, anyMatch);
5320         return fn ? this.queryBy(fn) : this.data.clone();
5321     },
5322
5323     /**
5324      * Query by a function. The specified function will be called with each
5325      * record in this data source. If the function returns true the record is included
5326      * in the results.
5327      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5328      * @param {Object} scope (optional) The scope of the function (defaults to this)
5329       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5330      **/
5331     queryBy : function(fn, scope){
5332         var data = this.snapshot || this.data;
5333         return data.filterBy(fn, scope||this);
5334     },
5335
5336     /**
5337      * Collects unique values for a particular dataIndex from this store.
5338      * @param {String} dataIndex The property to collect
5339      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5340      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5341      * @return {Array} An array of the unique values
5342      **/
5343     collect : function(dataIndex, allowNull, bypassFilter){
5344         var d = (bypassFilter === true && this.snapshot) ?
5345                 this.snapshot.items : this.data.items;
5346         var v, sv, r = [], l = {};
5347         for(var i = 0, len = d.length; i < len; i++){
5348             v = d[i].data[dataIndex];
5349             sv = String(v);
5350             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5351                 l[sv] = true;
5352                 r[r.length] = v;
5353             }
5354         }
5355         return r;
5356     },
5357
5358     /**
5359      * Revert to a view of the Record cache with no filtering applied.
5360      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5361      */
5362     clearFilter : function(suppressEvent){
5363         if(this.snapshot && this.snapshot != this.data){
5364             this.data = this.snapshot;
5365             delete this.snapshot;
5366             if(suppressEvent !== true){
5367                 this.fireEvent("datachanged", this);
5368             }
5369         }
5370     },
5371
5372     // private
5373     afterEdit : function(record){
5374         if(this.modified.indexOf(record) == -1){
5375             this.modified.push(record);
5376         }
5377         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5378     },
5379     
5380     // private
5381     afterReject : function(record){
5382         this.modified.remove(record);
5383         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5384     },
5385
5386     // private
5387     afterCommit : function(record){
5388         this.modified.remove(record);
5389         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5390     },
5391
5392     /**
5393      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5394      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5395      */
5396     commitChanges : function(){
5397         var m = this.modified.slice(0);
5398         this.modified = [];
5399         for(var i = 0, len = m.length; i < len; i++){
5400             m[i].commit();
5401         }
5402     },
5403
5404     /**
5405      * Cancel outstanding changes on all changed records.
5406      */
5407     rejectChanges : function(){
5408         var m = this.modified.slice(0);
5409         this.modified = [];
5410         for(var i = 0, len = m.length; i < len; i++){
5411             m[i].reject();
5412         }
5413     },
5414
5415     onMetaChange : function(meta, rtype, o){
5416         this.recordType = rtype;
5417         this.fields = rtype.prototype.fields;
5418         delete this.snapshot;
5419         this.sortInfo = meta.sortInfo || this.sortInfo;
5420         this.modified = [];
5421         this.fireEvent('metachange', this, this.reader.meta);
5422     },
5423     
5424     moveIndex : function(data, type)
5425     {
5426         var index = this.indexOf(data);
5427         
5428         var newIndex = index + type;
5429         
5430         this.remove(data);
5431         
5432         this.insert(newIndex, data);
5433         
5434     }
5435 });/*
5436  * Based on:
5437  * Ext JS Library 1.1.1
5438  * Copyright(c) 2006-2007, Ext JS, LLC.
5439  *
5440  * Originally Released Under LGPL - original licence link has changed is not relivant.
5441  *
5442  * Fork - LGPL
5443  * <script type="text/javascript">
5444  */
5445
5446 /**
5447  * @class Roo.data.SimpleStore
5448  * @extends Roo.data.Store
5449  * Small helper class to make creating Stores from Array data easier.
5450  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5451  * @cfg {Array} fields An array of field definition objects, or field name strings.
5452  * @cfg {Array} data The multi-dimensional array of data
5453  * @constructor
5454  * @param {Object} config
5455  */
5456 Roo.data.SimpleStore = function(config){
5457     Roo.data.SimpleStore.superclass.constructor.call(this, {
5458         isLocal : true,
5459         reader: new Roo.data.ArrayReader({
5460                 id: config.id
5461             },
5462             Roo.data.Record.create(config.fields)
5463         ),
5464         proxy : new Roo.data.MemoryProxy(config.data)
5465     });
5466     this.load();
5467 };
5468 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5469  * Based on:
5470  * Ext JS Library 1.1.1
5471  * Copyright(c) 2006-2007, Ext JS, LLC.
5472  *
5473  * Originally Released Under LGPL - original licence link has changed is not relivant.
5474  *
5475  * Fork - LGPL
5476  * <script type="text/javascript">
5477  */
5478
5479 /**
5480 /**
5481  * @extends Roo.data.Store
5482  * @class Roo.data.JsonStore
5483  * Small helper class to make creating Stores for JSON data easier. <br/>
5484 <pre><code>
5485 var store = new Roo.data.JsonStore({
5486     url: 'get-images.php',
5487     root: 'images',
5488     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5489 });
5490 </code></pre>
5491  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5492  * JsonReader and HttpProxy (unless inline data is provided).</b>
5493  * @cfg {Array} fields An array of field definition objects, or field name strings.
5494  * @constructor
5495  * @param {Object} config
5496  */
5497 Roo.data.JsonStore = function(c){
5498     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5499         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5500         reader: new Roo.data.JsonReader(c, c.fields)
5501     }));
5502 };
5503 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5504  * Based on:
5505  * Ext JS Library 1.1.1
5506  * Copyright(c) 2006-2007, Ext JS, LLC.
5507  *
5508  * Originally Released Under LGPL - original licence link has changed is not relivant.
5509  *
5510  * Fork - LGPL
5511  * <script type="text/javascript">
5512  */
5513
5514  
5515 Roo.data.Field = function(config){
5516     if(typeof config == "string"){
5517         config = {name: config};
5518     }
5519     Roo.apply(this, config);
5520     
5521     if(!this.type){
5522         this.type = "auto";
5523     }
5524     
5525     var st = Roo.data.SortTypes;
5526     // named sortTypes are supported, here we look them up
5527     if(typeof this.sortType == "string"){
5528         this.sortType = st[this.sortType];
5529     }
5530     
5531     // set default sortType for strings and dates
5532     if(!this.sortType){
5533         switch(this.type){
5534             case "string":
5535                 this.sortType = st.asUCString;
5536                 break;
5537             case "date":
5538                 this.sortType = st.asDate;
5539                 break;
5540             default:
5541                 this.sortType = st.none;
5542         }
5543     }
5544
5545     // define once
5546     var stripRe = /[\$,%]/g;
5547
5548     // prebuilt conversion function for this field, instead of
5549     // switching every time we're reading a value
5550     if(!this.convert){
5551         var cv, dateFormat = this.dateFormat;
5552         switch(this.type){
5553             case "":
5554             case "auto":
5555             case undefined:
5556                 cv = function(v){ return v; };
5557                 break;
5558             case "string":
5559                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5560                 break;
5561             case "int":
5562                 cv = function(v){
5563                     return v !== undefined && v !== null && v !== '' ?
5564                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5565                     };
5566                 break;
5567             case "float":
5568                 cv = function(v){
5569                     return v !== undefined && v !== null && v !== '' ?
5570                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5571                     };
5572                 break;
5573             case "bool":
5574             case "boolean":
5575                 cv = function(v){ return v === true || v === "true" || v == 1; };
5576                 break;
5577             case "date":
5578                 cv = function(v){
5579                     if(!v){
5580                         return '';
5581                     }
5582                     if(v instanceof Date){
5583                         return v;
5584                     }
5585                     if(dateFormat){
5586                         if(dateFormat == "timestamp"){
5587                             return new Date(v*1000);
5588                         }
5589                         return Date.parseDate(v, dateFormat);
5590                     }
5591                     var parsed = Date.parse(v);
5592                     return parsed ? new Date(parsed) : null;
5593                 };
5594              break;
5595             
5596         }
5597         this.convert = cv;
5598     }
5599 };
5600
5601 Roo.data.Field.prototype = {
5602     dateFormat: null,
5603     defaultValue: "",
5604     mapping: null,
5605     sortType : null,
5606     sortDir : "ASC"
5607 };/*
5608  * Based on:
5609  * Ext JS Library 1.1.1
5610  * Copyright(c) 2006-2007, Ext JS, LLC.
5611  *
5612  * Originally Released Under LGPL - original licence link has changed is not relivant.
5613  *
5614  * Fork - LGPL
5615  * <script type="text/javascript">
5616  */
5617  
5618 // Base class for reading structured data from a data source.  This class is intended to be
5619 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5620
5621 /**
5622  * @class Roo.data.DataReader
5623  * Base class for reading structured data from a data source.  This class is intended to be
5624  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5625  */
5626
5627 Roo.data.DataReader = function(meta, recordType){
5628     
5629     this.meta = meta;
5630     
5631     this.recordType = recordType instanceof Array ? 
5632         Roo.data.Record.create(recordType) : recordType;
5633 };
5634
5635 Roo.data.DataReader.prototype = {
5636      /**
5637      * Create an empty record
5638      * @param {Object} data (optional) - overlay some values
5639      * @return {Roo.data.Record} record created.
5640      */
5641     newRow :  function(d) {
5642         var da =  {};
5643         this.recordType.prototype.fields.each(function(c) {
5644             switch( c.type) {
5645                 case 'int' : da[c.name] = 0; break;
5646                 case 'date' : da[c.name] = new Date(); break;
5647                 case 'float' : da[c.name] = 0.0; break;
5648                 case 'boolean' : da[c.name] = false; break;
5649                 default : da[c.name] = ""; break;
5650             }
5651             
5652         });
5653         return new this.recordType(Roo.apply(da, d));
5654     }
5655     
5656 };/*
5657  * Based on:
5658  * Ext JS Library 1.1.1
5659  * Copyright(c) 2006-2007, Ext JS, LLC.
5660  *
5661  * Originally Released Under LGPL - original licence link has changed is not relivant.
5662  *
5663  * Fork - LGPL
5664  * <script type="text/javascript">
5665  */
5666
5667 /**
5668  * @class Roo.data.DataProxy
5669  * @extends Roo.data.Observable
5670  * This class is an abstract base class for implementations which provide retrieval of
5671  * unformatted data objects.<br>
5672  * <p>
5673  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5674  * (of the appropriate type which knows how to parse the data object) to provide a block of
5675  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5676  * <p>
5677  * Custom implementations must implement the load method as described in
5678  * {@link Roo.data.HttpProxy#load}.
5679  */
5680 Roo.data.DataProxy = function(){
5681     this.addEvents({
5682         /**
5683          * @event beforeload
5684          * Fires before a network request is made to retrieve a data object.
5685          * @param {Object} This DataProxy object.
5686          * @param {Object} params The params parameter to the load function.
5687          */
5688         beforeload : true,
5689         /**
5690          * @event load
5691          * Fires before the load method's callback is called.
5692          * @param {Object} This DataProxy object.
5693          * @param {Object} o The data object.
5694          * @param {Object} arg The callback argument object passed to the load function.
5695          */
5696         load : true,
5697         /**
5698          * @event loadexception
5699          * Fires if an Exception occurs during data retrieval.
5700          * @param {Object} This DataProxy object.
5701          * @param {Object} o The data object.
5702          * @param {Object} arg The callback argument object passed to the load function.
5703          * @param {Object} e The Exception.
5704          */
5705         loadexception : true
5706     });
5707     Roo.data.DataProxy.superclass.constructor.call(this);
5708 };
5709
5710 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5711
5712     /**
5713      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5714      */
5715 /*
5716  * Based on:
5717  * Ext JS Library 1.1.1
5718  * Copyright(c) 2006-2007, Ext JS, LLC.
5719  *
5720  * Originally Released Under LGPL - original licence link has changed is not relivant.
5721  *
5722  * Fork - LGPL
5723  * <script type="text/javascript">
5724  */
5725 /**
5726  * @class Roo.data.MemoryProxy
5727  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5728  * to the Reader when its load method is called.
5729  * @constructor
5730  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5731  */
5732 Roo.data.MemoryProxy = function(data){
5733     if (data.data) {
5734         data = data.data;
5735     }
5736     Roo.data.MemoryProxy.superclass.constructor.call(this);
5737     this.data = data;
5738 };
5739
5740 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5741     /**
5742      * Load data from the requested source (in this case an in-memory
5743      * data object passed to the constructor), read the data object into
5744      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5745      * process that block using the passed callback.
5746      * @param {Object} params This parameter is not used by the MemoryProxy class.
5747      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5748      * object into a block of Roo.data.Records.
5749      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5750      * The function must be passed <ul>
5751      * <li>The Record block object</li>
5752      * <li>The "arg" argument from the load function</li>
5753      * <li>A boolean success indicator</li>
5754      * </ul>
5755      * @param {Object} scope The scope in which to call the callback
5756      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5757      */
5758     load : function(params, reader, callback, scope, arg){
5759         params = params || {};
5760         var result;
5761         try {
5762             result = reader.readRecords(this.data);
5763         }catch(e){
5764             this.fireEvent("loadexception", this, arg, null, e);
5765             callback.call(scope, null, arg, false);
5766             return;
5767         }
5768         callback.call(scope, result, arg, true);
5769     },
5770     
5771     // private
5772     update : function(params, records){
5773         
5774     }
5775 });/*
5776  * Based on:
5777  * Ext JS Library 1.1.1
5778  * Copyright(c) 2006-2007, Ext JS, LLC.
5779  *
5780  * Originally Released Under LGPL - original licence link has changed is not relivant.
5781  *
5782  * Fork - LGPL
5783  * <script type="text/javascript">
5784  */
5785 /**
5786  * @class Roo.data.HttpProxy
5787  * @extends Roo.data.DataProxy
5788  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5789  * configured to reference a certain URL.<br><br>
5790  * <p>
5791  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5792  * from which the running page was served.<br><br>
5793  * <p>
5794  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5795  * <p>
5796  * Be aware that to enable the browser to parse an XML document, the server must set
5797  * the Content-Type header in the HTTP response to "text/xml".
5798  * @constructor
5799  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5800  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5801  * will be used to make the request.
5802  */
5803 Roo.data.HttpProxy = function(conn){
5804     Roo.data.HttpProxy.superclass.constructor.call(this);
5805     // is conn a conn config or a real conn?
5806     this.conn = conn;
5807     this.useAjax = !conn || !conn.events;
5808   
5809 };
5810
5811 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5812     // thse are take from connection...
5813     
5814     /**
5815      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5816      */
5817     /**
5818      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5819      * extra parameters to each request made by this object. (defaults to undefined)
5820      */
5821     /**
5822      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5823      *  to each request made by this object. (defaults to undefined)
5824      */
5825     /**
5826      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
5827      */
5828     /**
5829      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5830      */
5831      /**
5832      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5833      * @type Boolean
5834      */
5835   
5836
5837     /**
5838      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5839      * @type Boolean
5840      */
5841     /**
5842      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5843      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5844      * a finer-grained basis than the DataProxy events.
5845      */
5846     getConnection : function(){
5847         return this.useAjax ? Roo.Ajax : this.conn;
5848     },
5849
5850     /**
5851      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5852      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5853      * process that block using the passed callback.
5854      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5855      * for the request to the remote server.
5856      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5857      * object into a block of Roo.data.Records.
5858      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5859      * The function must be passed <ul>
5860      * <li>The Record block object</li>
5861      * <li>The "arg" argument from the load function</li>
5862      * <li>A boolean success indicator</li>
5863      * </ul>
5864      * @param {Object} scope The scope in which to call the callback
5865      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5866      */
5867     load : function(params, reader, callback, scope, arg){
5868         if(this.fireEvent("beforeload", this, params) !== false){
5869             var  o = {
5870                 params : params || {},
5871                 request: {
5872                     callback : callback,
5873                     scope : scope,
5874                     arg : arg
5875                 },
5876                 reader: reader,
5877                 callback : this.loadResponse,
5878                 scope: this
5879             };
5880             if(this.useAjax){
5881                 Roo.applyIf(o, this.conn);
5882                 if(this.activeRequest){
5883                     Roo.Ajax.abort(this.activeRequest);
5884                 }
5885                 this.activeRequest = Roo.Ajax.request(o);
5886             }else{
5887                 this.conn.request(o);
5888             }
5889         }else{
5890             callback.call(scope||this, null, arg, false);
5891         }
5892     },
5893
5894     // private
5895     loadResponse : function(o, success, response){
5896         delete this.activeRequest;
5897         if(!success){
5898             this.fireEvent("loadexception", this, o, response);
5899             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5900             return;
5901         }
5902         var result;
5903         try {
5904             result = o.reader.read(response);
5905         }catch(e){
5906             this.fireEvent("loadexception", this, o, response, e);
5907             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5908             return;
5909         }
5910         
5911         this.fireEvent("load", this, o, o.request.arg);
5912         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5913     },
5914
5915     // private
5916     update : function(dataSet){
5917
5918     },
5919
5920     // private
5921     updateResponse : function(dataSet){
5922
5923     }
5924 });/*
5925  * Based on:
5926  * Ext JS Library 1.1.1
5927  * Copyright(c) 2006-2007, Ext JS, LLC.
5928  *
5929  * Originally Released Under LGPL - original licence link has changed is not relivant.
5930  *
5931  * Fork - LGPL
5932  * <script type="text/javascript">
5933  */
5934
5935 /**
5936  * @class Roo.data.ScriptTagProxy
5937  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5938  * other than the originating domain of the running page.<br><br>
5939  * <p>
5940  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
5941  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5942  * <p>
5943  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5944  * source code that is used as the source inside a &lt;script> tag.<br><br>
5945  * <p>
5946  * In order for the browser to process the returned data, the server must wrap the data object
5947  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5948  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5949  * depending on whether the callback name was passed:
5950  * <p>
5951  * <pre><code>
5952 boolean scriptTag = false;
5953 String cb = request.getParameter("callback");
5954 if (cb != null) {
5955     scriptTag = true;
5956     response.setContentType("text/javascript");
5957 } else {
5958     response.setContentType("application/x-json");
5959 }
5960 Writer out = response.getWriter();
5961 if (scriptTag) {
5962     out.write(cb + "(");
5963 }
5964 out.print(dataBlock.toJsonString());
5965 if (scriptTag) {
5966     out.write(");");
5967 }
5968 </pre></code>
5969  *
5970  * @constructor
5971  * @param {Object} config A configuration object.
5972  */
5973 Roo.data.ScriptTagProxy = function(config){
5974     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5975     Roo.apply(this, config);
5976     this.head = document.getElementsByTagName("head")[0];
5977 };
5978
5979 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5980
5981 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5982     /**
5983      * @cfg {String} url The URL from which to request the data object.
5984      */
5985     /**
5986      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5987      */
5988     timeout : 30000,
5989     /**
5990      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5991      * the server the name of the callback function set up by the load call to process the returned data object.
5992      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5993      * javascript output which calls this named function passing the data object as its only parameter.
5994      */
5995     callbackParam : "callback",
5996     /**
5997      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5998      * name to the request.
5999      */
6000     nocache : true,
6001
6002     /**
6003      * Load data from the configured URL, read the data object into
6004      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
6005      * process that block using the passed callback.
6006      * @param {Object} params An object containing properties which are to be used as HTTP parameters
6007      * for the request to the remote server.
6008      * @param {Roo.data.DataReader} reader The Reader object which converts the data
6009      * object into a block of Roo.data.Records.
6010      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
6011      * The function must be passed <ul>
6012      * <li>The Record block object</li>
6013      * <li>The "arg" argument from the load function</li>
6014      * <li>A boolean success indicator</li>
6015      * </ul>
6016      * @param {Object} scope The scope in which to call the callback
6017      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
6018      */
6019     load : function(params, reader, callback, scope, arg){
6020         if(this.fireEvent("beforeload", this, params) !== false){
6021
6022             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
6023
6024             var url = this.url;
6025             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
6026             if(this.nocache){
6027                 url += "&_dc=" + (new Date().getTime());
6028             }
6029             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
6030             var trans = {
6031                 id : transId,
6032                 cb : "stcCallback"+transId,
6033                 scriptId : "stcScript"+transId,
6034                 params : params,
6035                 arg : arg,
6036                 url : url,
6037                 callback : callback,
6038                 scope : scope,
6039                 reader : reader
6040             };
6041             var conn = this;
6042
6043             window[trans.cb] = function(o){
6044                 conn.handleResponse(o, trans);
6045             };
6046
6047             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
6048
6049             if(this.autoAbort !== false){
6050                 this.abort();
6051             }
6052
6053             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6054
6055             var script = document.createElement("script");
6056             script.setAttribute("src", url);
6057             script.setAttribute("type", "text/javascript");
6058             script.setAttribute("id", trans.scriptId);
6059             this.head.appendChild(script);
6060
6061             this.trans = trans;
6062         }else{
6063             callback.call(scope||this, null, arg, false);
6064         }
6065     },
6066
6067     // private
6068     isLoading : function(){
6069         return this.trans ? true : false;
6070     },
6071
6072     /**
6073      * Abort the current server request.
6074      */
6075     abort : function(){
6076         if(this.isLoading()){
6077             this.destroyTrans(this.trans);
6078         }
6079     },
6080
6081     // private
6082     destroyTrans : function(trans, isLoaded){
6083         this.head.removeChild(document.getElementById(trans.scriptId));
6084         clearTimeout(trans.timeoutId);
6085         if(isLoaded){
6086             window[trans.cb] = undefined;
6087             try{
6088                 delete window[trans.cb];
6089             }catch(e){}
6090         }else{
6091             // if hasn't been loaded, wait for load to remove it to prevent script error
6092             window[trans.cb] = function(){
6093                 window[trans.cb] = undefined;
6094                 try{
6095                     delete window[trans.cb];
6096                 }catch(e){}
6097             };
6098         }
6099     },
6100
6101     // private
6102     handleResponse : function(o, trans){
6103         this.trans = false;
6104         this.destroyTrans(trans, true);
6105         var result;
6106         try {
6107             result = trans.reader.readRecords(o);
6108         }catch(e){
6109             this.fireEvent("loadexception", this, o, trans.arg, e);
6110             trans.callback.call(trans.scope||window, null, trans.arg, false);
6111             return;
6112         }
6113         this.fireEvent("load", this, o, trans.arg);
6114         trans.callback.call(trans.scope||window, result, trans.arg, true);
6115     },
6116
6117     // private
6118     handleFailure : function(trans){
6119         this.trans = false;
6120         this.destroyTrans(trans, false);
6121         this.fireEvent("loadexception", this, null, trans.arg);
6122         trans.callback.call(trans.scope||window, null, trans.arg, false);
6123     }
6124 });/*
6125  * Based on:
6126  * Ext JS Library 1.1.1
6127  * Copyright(c) 2006-2007, Ext JS, LLC.
6128  *
6129  * Originally Released Under LGPL - original licence link has changed is not relivant.
6130  *
6131  * Fork - LGPL
6132  * <script type="text/javascript">
6133  */
6134
6135 /**
6136  * @class Roo.data.JsonReader
6137  * @extends Roo.data.DataReader
6138  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6139  * based on mappings in a provided Roo.data.Record constructor.
6140  * 
6141  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6142  * in the reply previously. 
6143  * 
6144  * <p>
6145  * Example code:
6146  * <pre><code>
6147 var RecordDef = Roo.data.Record.create([
6148     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6149     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6150 ]);
6151 var myReader = new Roo.data.JsonReader({
6152     totalProperty: "results",    // The property which contains the total dataset size (optional)
6153     root: "rows",                // The property which contains an Array of row objects
6154     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6155 }, RecordDef);
6156 </code></pre>
6157  * <p>
6158  * This would consume a JSON file like this:
6159  * <pre><code>
6160 { 'results': 2, 'rows': [
6161     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6162     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6163 }
6164 </code></pre>
6165  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6166  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6167  * paged from the remote server.
6168  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6169  * @cfg {String} root name of the property which contains the Array of row objects.
6170  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6171  * @constructor
6172  * Create a new JsonReader
6173  * @param {Object} meta Metadata configuration options
6174  * @param {Object} recordType Either an Array of field definition objects,
6175  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6176  */
6177 Roo.data.JsonReader = function(meta, recordType){
6178     
6179     meta = meta || {};
6180     // set some defaults:
6181     Roo.applyIf(meta, {
6182         totalProperty: 'total',
6183         successProperty : 'success',
6184         root : 'data',
6185         id : 'id'
6186     });
6187     
6188     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6189 };
6190 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6191     
6192     /**
6193      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6194      * Used by Store query builder to append _requestMeta to params.
6195      * 
6196      */
6197     metaFromRemote : false,
6198     /**
6199      * This method is only used by a DataProxy which has retrieved data from a remote server.
6200      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6201      * @return {Object} data A data block which is used by an Roo.data.Store object as
6202      * a cache of Roo.data.Records.
6203      */
6204     read : function(response){
6205         var json = response.responseText;
6206        
6207         var o = /* eval:var:o */ eval("("+json+")");
6208         if(!o) {
6209             throw {message: "JsonReader.read: Json object not found"};
6210         }
6211         
6212         if(o.metaData){
6213             
6214             delete this.ef;
6215             this.metaFromRemote = true;
6216             this.meta = o.metaData;
6217             this.recordType = Roo.data.Record.create(o.metaData.fields);
6218             this.onMetaChange(this.meta, this.recordType, o);
6219         }
6220         return this.readRecords(o);
6221     },
6222
6223     // private function a store will implement
6224     onMetaChange : function(meta, recordType, o){
6225
6226     },
6227
6228     /**
6229          * @ignore
6230          */
6231     simpleAccess: function(obj, subsc) {
6232         return obj[subsc];
6233     },
6234
6235         /**
6236          * @ignore
6237          */
6238     getJsonAccessor: function(){
6239         var re = /[\[\.]/;
6240         return function(expr) {
6241             try {
6242                 return(re.test(expr))
6243                     ? new Function("obj", "return obj." + expr)
6244                     : function(obj){
6245                         return obj[expr];
6246                     };
6247             } catch(e){}
6248             return Roo.emptyFn;
6249         };
6250     }(),
6251
6252     /**
6253      * Create a data block containing Roo.data.Records from an XML document.
6254      * @param {Object} o An object which contains an Array of row objects in the property specified
6255      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6256      * which contains the total size of the dataset.
6257      * @return {Object} data A data block which is used by an Roo.data.Store object as
6258      * a cache of Roo.data.Records.
6259      */
6260     readRecords : function(o){
6261         /**
6262          * After any data loads, the raw JSON data is available for further custom processing.
6263          * @type Object
6264          */
6265         this.o = o;
6266         var s = this.meta, Record = this.recordType,
6267             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length: 0;
6268
6269 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6270         if (!this.ef) {
6271             if(s.totalProperty) {
6272                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6273                 }
6274                 if(s.successProperty) {
6275                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6276                 }
6277                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6278                 if (s.id) {
6279                         var g = this.getJsonAccessor(s.id);
6280                         this.getId = function(rec) {
6281                                 var r = g(rec);  
6282                                 return (r === undefined || r === "") ? null : r;
6283                         };
6284                 } else {
6285                         this.getId = function(){return null;};
6286                 }
6287             this.ef = [];
6288             for(var jj = 0; jj < fl; jj++){
6289                 f = fi[jj];
6290                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6291                 this.ef[jj] = this.getJsonAccessor(map);
6292             }
6293         }
6294
6295         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6296         if(s.totalProperty){
6297             var vt = parseInt(this.getTotal(o), 10);
6298             if(!isNaN(vt)){
6299                 totalRecords = vt;
6300             }
6301         }
6302         if(s.successProperty){
6303             var vs = this.getSuccess(o);
6304             if(vs === false || vs === 'false'){
6305                 success = false;
6306             }
6307         }
6308         var records = [];
6309         for(var i = 0; i < c; i++){
6310                 var n = root[i];
6311             var values = {};
6312             var id = this.getId(n);
6313             for(var j = 0; j < fl; j++){
6314                 f = fi[j];
6315             var v = this.ef[j](n);
6316             if (!f.convert) {
6317                 Roo.log('missing convert for ' + f.name);
6318                 Roo.log(f);
6319                 continue;
6320             }
6321             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6322             }
6323             var record = new Record(values, id);
6324             record.json = n;
6325             records[i] = record;
6326         }
6327         return {
6328             raw : o,
6329             success : success,
6330             records : records,
6331             totalRecords : totalRecords
6332         };
6333     }
6334 });/*
6335  * Based on:
6336  * Ext JS Library 1.1.1
6337  * Copyright(c) 2006-2007, Ext JS, LLC.
6338  *
6339  * Originally Released Under LGPL - original licence link has changed is not relivant.
6340  *
6341  * Fork - LGPL
6342  * <script type="text/javascript">
6343  */
6344
6345 /**
6346  * @class Roo.data.XmlReader
6347  * @extends Roo.data.DataReader
6348  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6349  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6350  * <p>
6351  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6352  * header in the HTTP response must be set to "text/xml".</em>
6353  * <p>
6354  * Example code:
6355  * <pre><code>
6356 var RecordDef = Roo.data.Record.create([
6357    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6358    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6359 ]);
6360 var myReader = new Roo.data.XmlReader({
6361    totalRecords: "results", // The element which contains the total dataset size (optional)
6362    record: "row",           // The repeated element which contains row information
6363    id: "id"                 // The element within the row that provides an ID for the record (optional)
6364 }, RecordDef);
6365 </code></pre>
6366  * <p>
6367  * This would consume an XML file like this:
6368  * <pre><code>
6369 &lt;?xml?>
6370 &lt;dataset>
6371  &lt;results>2&lt;/results>
6372  &lt;row>
6373    &lt;id>1&lt;/id>
6374    &lt;name>Bill&lt;/name>
6375    &lt;occupation>Gardener&lt;/occupation>
6376  &lt;/row>
6377  &lt;row>
6378    &lt;id>2&lt;/id>
6379    &lt;name>Ben&lt;/name>
6380    &lt;occupation>Horticulturalist&lt;/occupation>
6381  &lt;/row>
6382 &lt;/dataset>
6383 </code></pre>
6384  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6385  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6386  * paged from the remote server.
6387  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6388  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6389  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6390  * a record identifier value.
6391  * @constructor
6392  * Create a new XmlReader
6393  * @param {Object} meta Metadata configuration options
6394  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6395  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6396  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6397  */
6398 Roo.data.XmlReader = function(meta, recordType){
6399     meta = meta || {};
6400     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6401 };
6402 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6403     /**
6404      * This method is only used by a DataProxy which has retrieved data from a remote server.
6405          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6406          * to contain a method called 'responseXML' that returns an XML document object.
6407      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6408      * a cache of Roo.data.Records.
6409      */
6410     read : function(response){
6411         var doc = response.responseXML;
6412         if(!doc) {
6413             throw {message: "XmlReader.read: XML Document not available"};
6414         }
6415         return this.readRecords(doc);
6416     },
6417
6418     /**
6419      * Create a data block containing Roo.data.Records from an XML document.
6420          * @param {Object} doc A parsed XML document.
6421      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6422      * a cache of Roo.data.Records.
6423      */
6424     readRecords : function(doc){
6425         /**
6426          * After any data loads/reads, the raw XML Document is available for further custom processing.
6427          * @type XMLDocument
6428          */
6429         this.xmlData = doc;
6430         var root = doc.documentElement || doc;
6431         var q = Roo.DomQuery;
6432         var recordType = this.recordType, fields = recordType.prototype.fields;
6433         var sid = this.meta.id;
6434         var totalRecords = 0, success = true;
6435         if(this.meta.totalRecords){
6436             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6437         }
6438         
6439         if(this.meta.success){
6440             var sv = q.selectValue(this.meta.success, root, true);
6441             success = sv !== false && sv !== 'false';
6442         }
6443         var records = [];
6444         var ns = q.select(this.meta.record, root);
6445         for(var i = 0, len = ns.length; i < len; i++) {
6446                 var n = ns[i];
6447                 var values = {};
6448                 var id = sid ? q.selectValue(sid, n) : undefined;
6449                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6450                     var f = fields.items[j];
6451                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6452                     v = f.convert(v);
6453                     values[f.name] = v;
6454                 }
6455                 var record = new recordType(values, id);
6456                 record.node = n;
6457                 records[records.length] = record;
6458             }
6459
6460             return {
6461                 success : success,
6462                 records : records,
6463                 totalRecords : totalRecords || records.length
6464             };
6465     }
6466 });/*
6467  * Based on:
6468  * Ext JS Library 1.1.1
6469  * Copyright(c) 2006-2007, Ext JS, LLC.
6470  *
6471  * Originally Released Under LGPL - original licence link has changed is not relivant.
6472  *
6473  * Fork - LGPL
6474  * <script type="text/javascript">
6475  */
6476
6477 /**
6478  * @class Roo.data.ArrayReader
6479  * @extends Roo.data.DataReader
6480  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6481  * Each element of that Array represents a row of data fields. The
6482  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6483  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6484  * <p>
6485  * Example code:.
6486  * <pre><code>
6487 var RecordDef = Roo.data.Record.create([
6488     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6489     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6490 ]);
6491 var myReader = new Roo.data.ArrayReader({
6492     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6493 }, RecordDef);
6494 </code></pre>
6495  * <p>
6496  * This would consume an Array like this:
6497  * <pre><code>
6498 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6499   </code></pre>
6500  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6501  * @constructor
6502  * Create a new JsonReader
6503  * @param {Object} meta Metadata configuration options.
6504  * @param {Object} recordType Either an Array of field definition objects
6505  * as specified to {@link Roo.data.Record#create},
6506  * or an {@link Roo.data.Record} object
6507  * created using {@link Roo.data.Record#create}.
6508  */
6509 Roo.data.ArrayReader = function(meta, recordType){
6510     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6511 };
6512
6513 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6514     /**
6515      * Create a data block containing Roo.data.Records from an XML document.
6516      * @param {Object} o An Array of row objects which represents the dataset.
6517      * @return {Object} data A data block which is used by an Roo.data.Store object as
6518      * a cache of Roo.data.Records.
6519      */
6520     readRecords : function(o){
6521         var sid = this.meta ? this.meta.id : null;
6522         var recordType = this.recordType, fields = recordType.prototype.fields;
6523         var records = [];
6524         var root = o;
6525             for(var i = 0; i < root.length; i++){
6526                     var n = root[i];
6527                 var values = {};
6528                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6529                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6530                 var f = fields.items[j];
6531                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6532                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6533                 v = f.convert(v);
6534                 values[f.name] = v;
6535             }
6536                 var record = new recordType(values, id);
6537                 record.json = n;
6538                 records[records.length] = record;
6539             }
6540             return {
6541                 records : records,
6542                 totalRecords : records.length
6543             };
6544     }
6545 });/*
6546  * Based on:
6547  * Ext JS Library 1.1.1
6548  * Copyright(c) 2006-2007, Ext JS, LLC.
6549  *
6550  * Originally Released Under LGPL - original licence link has changed is not relivant.
6551  *
6552  * Fork - LGPL
6553  * <script type="text/javascript">
6554  */
6555
6556
6557 /**
6558  * @class Roo.data.Tree
6559  * @extends Roo.util.Observable
6560  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6561  * in the tree have most standard DOM functionality.
6562  * @constructor
6563  * @param {Node} root (optional) The root node
6564  */
6565 Roo.data.Tree = function(root){
6566    this.nodeHash = {};
6567    /**
6568     * The root node for this tree
6569     * @type Node
6570     */
6571    this.root = null;
6572    if(root){
6573        this.setRootNode(root);
6574    }
6575    this.addEvents({
6576        /**
6577         * @event append
6578         * Fires when a new child node is appended to a node in this tree.
6579         * @param {Tree} tree The owner tree
6580         * @param {Node} parent The parent node
6581         * @param {Node} node The newly appended node
6582         * @param {Number} index The index of the newly appended node
6583         */
6584        "append" : true,
6585        /**
6586         * @event remove
6587         * Fires when a child node is removed from a node in this tree.
6588         * @param {Tree} tree The owner tree
6589         * @param {Node} parent The parent node
6590         * @param {Node} node The child node removed
6591         */
6592        "remove" : true,
6593        /**
6594         * @event move
6595         * Fires when a node is moved to a new location in the tree
6596         * @param {Tree} tree The owner tree
6597         * @param {Node} node The node moved
6598         * @param {Node} oldParent The old parent of this node
6599         * @param {Node} newParent The new parent of this node
6600         * @param {Number} index The index it was moved to
6601         */
6602        "move" : true,
6603        /**
6604         * @event insert
6605         * Fires when a new child node is inserted in a node in this tree.
6606         * @param {Tree} tree The owner tree
6607         * @param {Node} parent The parent node
6608         * @param {Node} node The child node inserted
6609         * @param {Node} refNode The child node the node was inserted before
6610         */
6611        "insert" : true,
6612        /**
6613         * @event beforeappend
6614         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6615         * @param {Tree} tree The owner tree
6616         * @param {Node} parent The parent node
6617         * @param {Node} node The child node to be appended
6618         */
6619        "beforeappend" : true,
6620        /**
6621         * @event beforeremove
6622         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6623         * @param {Tree} tree The owner tree
6624         * @param {Node} parent The parent node
6625         * @param {Node} node The child node to be removed
6626         */
6627        "beforeremove" : true,
6628        /**
6629         * @event beforemove
6630         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6631         * @param {Tree} tree The owner tree
6632         * @param {Node} node The node being moved
6633         * @param {Node} oldParent The parent of the node
6634         * @param {Node} newParent The new parent the node is moving to
6635         * @param {Number} index The index it is being moved to
6636         */
6637        "beforemove" : true,
6638        /**
6639         * @event beforeinsert
6640         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6641         * @param {Tree} tree The owner tree
6642         * @param {Node} parent The parent node
6643         * @param {Node} node The child node to be inserted
6644         * @param {Node} refNode The child node the node is being inserted before
6645         */
6646        "beforeinsert" : true
6647    });
6648
6649     Roo.data.Tree.superclass.constructor.call(this);
6650 };
6651
6652 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6653     pathSeparator: "/",
6654
6655     proxyNodeEvent : function(){
6656         return this.fireEvent.apply(this, arguments);
6657     },
6658
6659     /**
6660      * Returns the root node for this tree.
6661      * @return {Node}
6662      */
6663     getRootNode : function(){
6664         return this.root;
6665     },
6666
6667     /**
6668      * Sets the root node for this tree.
6669      * @param {Node} node
6670      * @return {Node}
6671      */
6672     setRootNode : function(node){
6673         this.root = node;
6674         node.ownerTree = this;
6675         node.isRoot = true;
6676         this.registerNode(node);
6677         return node;
6678     },
6679
6680     /**
6681      * Gets a node in this tree by its id.
6682      * @param {String} id
6683      * @return {Node}
6684      */
6685     getNodeById : function(id){
6686         return this.nodeHash[id];
6687     },
6688
6689     registerNode : function(node){
6690         this.nodeHash[node.id] = node;
6691     },
6692
6693     unregisterNode : function(node){
6694         delete this.nodeHash[node.id];
6695     },
6696
6697     toString : function(){
6698         return "[Tree"+(this.id?" "+this.id:"")+"]";
6699     }
6700 });
6701
6702 /**
6703  * @class Roo.data.Node
6704  * @extends Roo.util.Observable
6705  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6706  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6707  * @constructor
6708  * @param {Object} attributes The attributes/config for the node
6709  */
6710 Roo.data.Node = function(attributes){
6711     /**
6712      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6713      * @type {Object}
6714      */
6715     this.attributes = attributes || {};
6716     this.leaf = this.attributes.leaf;
6717     /**
6718      * The node id. @type String
6719      */
6720     this.id = this.attributes.id;
6721     if(!this.id){
6722         this.id = Roo.id(null, "ynode-");
6723         this.attributes.id = this.id;
6724     }
6725      
6726     
6727     /**
6728      * All child nodes of this node. @type Array
6729      */
6730     this.childNodes = [];
6731     if(!this.childNodes.indexOf){ // indexOf is a must
6732         this.childNodes.indexOf = function(o){
6733             for(var i = 0, len = this.length; i < len; i++){
6734                 if(this[i] == o) {
6735                     return i;
6736                 }
6737             }
6738             return -1;
6739         };
6740     }
6741     /**
6742      * The parent node for this node. @type Node
6743      */
6744     this.parentNode = null;
6745     /**
6746      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6747      */
6748     this.firstChild = null;
6749     /**
6750      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6751      */
6752     this.lastChild = null;
6753     /**
6754      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6755      */
6756     this.previousSibling = null;
6757     /**
6758      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6759      */
6760     this.nextSibling = null;
6761
6762     this.addEvents({
6763        /**
6764         * @event append
6765         * Fires when a new child node is appended
6766         * @param {Tree} tree The owner tree
6767         * @param {Node} this This node
6768         * @param {Node} node The newly appended node
6769         * @param {Number} index The index of the newly appended node
6770         */
6771        "append" : true,
6772        /**
6773         * @event remove
6774         * Fires when a child node is removed
6775         * @param {Tree} tree The owner tree
6776         * @param {Node} this This node
6777         * @param {Node} node The removed node
6778         */
6779        "remove" : true,
6780        /**
6781         * @event move
6782         * Fires when this node is moved to a new location in the tree
6783         * @param {Tree} tree The owner tree
6784         * @param {Node} this This node
6785         * @param {Node} oldParent The old parent of this node
6786         * @param {Node} newParent The new parent of this node
6787         * @param {Number} index The index it was moved to
6788         */
6789        "move" : true,
6790        /**
6791         * @event insert
6792         * Fires when a new child node is inserted.
6793         * @param {Tree} tree The owner tree
6794         * @param {Node} this This node
6795         * @param {Node} node The child node inserted
6796         * @param {Node} refNode The child node the node was inserted before
6797         */
6798        "insert" : true,
6799        /**
6800         * @event beforeappend
6801         * Fires before a new child is appended, return false to cancel the append.
6802         * @param {Tree} tree The owner tree
6803         * @param {Node} this This node
6804         * @param {Node} node The child node to be appended
6805         */
6806        "beforeappend" : true,
6807        /**
6808         * @event beforeremove
6809         * Fires before a child is removed, return false to cancel the remove.
6810         * @param {Tree} tree The owner tree
6811         * @param {Node} this This node
6812         * @param {Node} node The child node to be removed
6813         */
6814        "beforeremove" : true,
6815        /**
6816         * @event beforemove
6817         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6818         * @param {Tree} tree The owner tree
6819         * @param {Node} this This node
6820         * @param {Node} oldParent The parent of this node
6821         * @param {Node} newParent The new parent this node is moving to
6822         * @param {Number} index The index it is being moved to
6823         */
6824        "beforemove" : true,
6825        /**
6826         * @event beforeinsert
6827         * Fires before a new child is inserted, return false to cancel the insert.
6828         * @param {Tree} tree The owner tree
6829         * @param {Node} this This node
6830         * @param {Node} node The child node to be inserted
6831         * @param {Node} refNode The child node the node is being inserted before
6832         */
6833        "beforeinsert" : true
6834    });
6835     this.listeners = this.attributes.listeners;
6836     Roo.data.Node.superclass.constructor.call(this);
6837 };
6838
6839 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6840     fireEvent : function(evtName){
6841         // first do standard event for this node
6842         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6843             return false;
6844         }
6845         // then bubble it up to the tree if the event wasn't cancelled
6846         var ot = this.getOwnerTree();
6847         if(ot){
6848             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6849                 return false;
6850             }
6851         }
6852         return true;
6853     },
6854
6855     /**
6856      * Returns true if this node is a leaf
6857      * @return {Boolean}
6858      */
6859     isLeaf : function(){
6860         return this.leaf === true;
6861     },
6862
6863     // private
6864     setFirstChild : function(node){
6865         this.firstChild = node;
6866     },
6867
6868     //private
6869     setLastChild : function(node){
6870         this.lastChild = node;
6871     },
6872
6873
6874     /**
6875      * Returns true if this node is the last child of its parent
6876      * @return {Boolean}
6877      */
6878     isLast : function(){
6879        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6880     },
6881
6882     /**
6883      * Returns true if this node is the first child of its parent
6884      * @return {Boolean}
6885      */
6886     isFirst : function(){
6887        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6888     },
6889
6890     hasChildNodes : function(){
6891         return !this.isLeaf() && this.childNodes.length > 0;
6892     },
6893
6894     /**
6895      * Insert node(s) as the last child node of this node.
6896      * @param {Node/Array} node The node or Array of nodes to append
6897      * @return {Node} The appended node if single append, or null if an array was passed
6898      */
6899     appendChild : function(node){
6900         var multi = false;
6901         if(node instanceof Array){
6902             multi = node;
6903         }else if(arguments.length > 1){
6904             multi = arguments;
6905         }
6906         // if passed an array or multiple args do them one by one
6907         if(multi){
6908             for(var i = 0, len = multi.length; i < len; i++) {
6909                 this.appendChild(multi[i]);
6910             }
6911         }else{
6912             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6913                 return false;
6914             }
6915             var index = this.childNodes.length;
6916             var oldParent = node.parentNode;
6917             // it's a move, make sure we move it cleanly
6918             if(oldParent){
6919                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6920                     return false;
6921                 }
6922                 oldParent.removeChild(node);
6923             }
6924             index = this.childNodes.length;
6925             if(index == 0){
6926                 this.setFirstChild(node);
6927             }
6928             this.childNodes.push(node);
6929             node.parentNode = this;
6930             var ps = this.childNodes[index-1];
6931             if(ps){
6932                 node.previousSibling = ps;
6933                 ps.nextSibling = node;
6934             }else{
6935                 node.previousSibling = null;
6936             }
6937             node.nextSibling = null;
6938             this.setLastChild(node);
6939             node.setOwnerTree(this.getOwnerTree());
6940             this.fireEvent("append", this.ownerTree, this, node, index);
6941             if(oldParent){
6942                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6943             }
6944             return node;
6945         }
6946     },
6947
6948     /**
6949      * Removes a child node from this node.
6950      * @param {Node} node The node to remove
6951      * @return {Node} The removed node
6952      */
6953     removeChild : function(node){
6954         var index = this.childNodes.indexOf(node);
6955         if(index == -1){
6956             return false;
6957         }
6958         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6959             return false;
6960         }
6961
6962         // remove it from childNodes collection
6963         this.childNodes.splice(index, 1);
6964
6965         // update siblings
6966         if(node.previousSibling){
6967             node.previousSibling.nextSibling = node.nextSibling;
6968         }
6969         if(node.nextSibling){
6970             node.nextSibling.previousSibling = node.previousSibling;
6971         }
6972
6973         // update child refs
6974         if(this.firstChild == node){
6975             this.setFirstChild(node.nextSibling);
6976         }
6977         if(this.lastChild == node){
6978             this.setLastChild(node.previousSibling);
6979         }
6980
6981         node.setOwnerTree(null);
6982         // clear any references from the node
6983         node.parentNode = null;
6984         node.previousSibling = null;
6985         node.nextSibling = null;
6986         this.fireEvent("remove", this.ownerTree, this, node);
6987         return node;
6988     },
6989
6990     /**
6991      * Inserts the first node before the second node in this nodes childNodes collection.
6992      * @param {Node} node The node to insert
6993      * @param {Node} refNode The node to insert before (if null the node is appended)
6994      * @return {Node} The inserted node
6995      */
6996     insertBefore : function(node, refNode){
6997         if(!refNode){ // like standard Dom, refNode can be null for append
6998             return this.appendChild(node);
6999         }
7000         // nothing to do
7001         if(node == refNode){
7002             return false;
7003         }
7004
7005         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
7006             return false;
7007         }
7008         var index = this.childNodes.indexOf(refNode);
7009         var oldParent = node.parentNode;
7010         var refIndex = index;
7011
7012         // when moving internally, indexes will change after remove
7013         if(oldParent == this && this.childNodes.indexOf(node) < index){
7014             refIndex--;
7015         }
7016
7017         // it's a move, make sure we move it cleanly
7018         if(oldParent){
7019             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
7020                 return false;
7021             }
7022             oldParent.removeChild(node);
7023         }
7024         if(refIndex == 0){
7025             this.setFirstChild(node);
7026         }
7027         this.childNodes.splice(refIndex, 0, node);
7028         node.parentNode = this;
7029         var ps = this.childNodes[refIndex-1];
7030         if(ps){
7031             node.previousSibling = ps;
7032             ps.nextSibling = node;
7033         }else{
7034             node.previousSibling = null;
7035         }
7036         node.nextSibling = refNode;
7037         refNode.previousSibling = node;
7038         node.setOwnerTree(this.getOwnerTree());
7039         this.fireEvent("insert", this.ownerTree, this, node, refNode);
7040         if(oldParent){
7041             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
7042         }
7043         return node;
7044     },
7045
7046     /**
7047      * Returns the child node at the specified index.
7048      * @param {Number} index
7049      * @return {Node}
7050      */
7051     item : function(index){
7052         return this.childNodes[index];
7053     },
7054
7055     /**
7056      * Replaces one child node in this node with another.
7057      * @param {Node} newChild The replacement node
7058      * @param {Node} oldChild The node to replace
7059      * @return {Node} The replaced node
7060      */
7061     replaceChild : function(newChild, oldChild){
7062         this.insertBefore(newChild, oldChild);
7063         this.removeChild(oldChild);
7064         return oldChild;
7065     },
7066
7067     /**
7068      * Returns the index of a child node
7069      * @param {Node} node
7070      * @return {Number} The index of the node or -1 if it was not found
7071      */
7072     indexOf : function(child){
7073         return this.childNodes.indexOf(child);
7074     },
7075
7076     /**
7077      * Returns the tree this node is in.
7078      * @return {Tree}
7079      */
7080     getOwnerTree : function(){
7081         // if it doesn't have one, look for one
7082         if(!this.ownerTree){
7083             var p = this;
7084             while(p){
7085                 if(p.ownerTree){
7086                     this.ownerTree = p.ownerTree;
7087                     break;
7088                 }
7089                 p = p.parentNode;
7090             }
7091         }
7092         return this.ownerTree;
7093     },
7094
7095     /**
7096      * Returns depth of this node (the root node has a depth of 0)
7097      * @return {Number}
7098      */
7099     getDepth : function(){
7100         var depth = 0;
7101         var p = this;
7102         while(p.parentNode){
7103             ++depth;
7104             p = p.parentNode;
7105         }
7106         return depth;
7107     },
7108
7109     // private
7110     setOwnerTree : function(tree){
7111         // if it's move, we need to update everyone
7112         if(tree != this.ownerTree){
7113             if(this.ownerTree){
7114                 this.ownerTree.unregisterNode(this);
7115             }
7116             this.ownerTree = tree;
7117             var cs = this.childNodes;
7118             for(var i = 0, len = cs.length; i < len; i++) {
7119                 cs[i].setOwnerTree(tree);
7120             }
7121             if(tree){
7122                 tree.registerNode(this);
7123             }
7124         }
7125     },
7126
7127     /**
7128      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7129      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7130      * @return {String} The path
7131      */
7132     getPath : function(attr){
7133         attr = attr || "id";
7134         var p = this.parentNode;
7135         var b = [this.attributes[attr]];
7136         while(p){
7137             b.unshift(p.attributes[attr]);
7138             p = p.parentNode;
7139         }
7140         var sep = this.getOwnerTree().pathSeparator;
7141         return sep + b.join(sep);
7142     },
7143
7144     /**
7145      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7146      * function call will be the scope provided or the current node. The arguments to the function
7147      * will be the args provided or the current node. If the function returns false at any point,
7148      * the bubble is stopped.
7149      * @param {Function} fn The function to call
7150      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7151      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7152      */
7153     bubble : function(fn, scope, args){
7154         var p = this;
7155         while(p){
7156             if(fn.call(scope || p, args || p) === false){
7157                 break;
7158             }
7159             p = p.parentNode;
7160         }
7161     },
7162
7163     /**
7164      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7165      * function call will be the scope provided or the current node. The arguments to the function
7166      * will be the args provided or the current node. If the function returns false at any point,
7167      * the cascade is stopped on that branch.
7168      * @param {Function} fn The function to call
7169      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7170      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7171      */
7172     cascade : function(fn, scope, args){
7173         if(fn.call(scope || this, args || this) !== false){
7174             var cs = this.childNodes;
7175             for(var i = 0, len = cs.length; i < len; i++) {
7176                 cs[i].cascade(fn, scope, args);
7177             }
7178         }
7179     },
7180
7181     /**
7182      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7183      * function call will be the scope provided or the current node. The arguments to the function
7184      * will be the args provided or the current node. If the function returns false at any point,
7185      * the iteration stops.
7186      * @param {Function} fn The function to call
7187      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7188      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7189      */
7190     eachChild : function(fn, scope, args){
7191         var cs = this.childNodes;
7192         for(var i = 0, len = cs.length; i < len; i++) {
7193                 if(fn.call(scope || this, args || cs[i]) === false){
7194                     break;
7195                 }
7196         }
7197     },
7198
7199     /**
7200      * Finds the first child that has the attribute with the specified value.
7201      * @param {String} attribute The attribute name
7202      * @param {Mixed} value The value to search for
7203      * @return {Node} The found child or null if none was found
7204      */
7205     findChild : function(attribute, value){
7206         var cs = this.childNodes;
7207         for(var i = 0, len = cs.length; i < len; i++) {
7208                 if(cs[i].attributes[attribute] == value){
7209                     return cs[i];
7210                 }
7211         }
7212         return null;
7213     },
7214
7215     /**
7216      * Finds the first child by a custom function. The child matches if the function passed
7217      * returns true.
7218      * @param {Function} fn
7219      * @param {Object} scope (optional)
7220      * @return {Node} The found child or null if none was found
7221      */
7222     findChildBy : function(fn, scope){
7223         var cs = this.childNodes;
7224         for(var i = 0, len = cs.length; i < len; i++) {
7225                 if(fn.call(scope||cs[i], cs[i]) === true){
7226                     return cs[i];
7227                 }
7228         }
7229         return null;
7230     },
7231
7232     /**
7233      * Sorts this nodes children using the supplied sort function
7234      * @param {Function} fn
7235      * @param {Object} scope (optional)
7236      */
7237     sort : function(fn, scope){
7238         var cs = this.childNodes;
7239         var len = cs.length;
7240         if(len > 0){
7241             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7242             cs.sort(sortFn);
7243             for(var i = 0; i < len; i++){
7244                 var n = cs[i];
7245                 n.previousSibling = cs[i-1];
7246                 n.nextSibling = cs[i+1];
7247                 if(i == 0){
7248                     this.setFirstChild(n);
7249                 }
7250                 if(i == len-1){
7251                     this.setLastChild(n);
7252                 }
7253             }
7254         }
7255     },
7256
7257     /**
7258      * Returns true if this node is an ancestor (at any point) of the passed node.
7259      * @param {Node} node
7260      * @return {Boolean}
7261      */
7262     contains : function(node){
7263         return node.isAncestor(this);
7264     },
7265
7266     /**
7267      * Returns true if the passed node is an ancestor (at any point) of this node.
7268      * @param {Node} node
7269      * @return {Boolean}
7270      */
7271     isAncestor : function(node){
7272         var p = this.parentNode;
7273         while(p){
7274             if(p == node){
7275                 return true;
7276             }
7277             p = p.parentNode;
7278         }
7279         return false;
7280     },
7281
7282     toString : function(){
7283         return "[Node"+(this.id?" "+this.id:"")+"]";
7284     }
7285 });/*
7286  * Based on:
7287  * Ext JS Library 1.1.1
7288  * Copyright(c) 2006-2007, Ext JS, LLC.
7289  *
7290  * Originally Released Under LGPL - original licence link has changed is not relivant.
7291  *
7292  * Fork - LGPL
7293  * <script type="text/javascript">
7294  */
7295  (function(){ 
7296 /**
7297  * @class Roo.Layer
7298  * @extends Roo.Element
7299  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7300  * automatic maintaining of shadow/shim positions.
7301  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7302  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7303  * you can pass a string with a CSS class name. False turns off the shadow.
7304  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7305  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7306  * @cfg {String} cls CSS class to add to the element
7307  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7308  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7309  * @constructor
7310  * @param {Object} config An object with config options.
7311  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7312  */
7313
7314 Roo.Layer = function(config, existingEl){
7315     config = config || {};
7316     var dh = Roo.DomHelper;
7317     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7318     if(existingEl){
7319         this.dom = Roo.getDom(existingEl);
7320     }
7321     if(!this.dom){
7322         var o = config.dh || {tag: "div", cls: "x-layer"};
7323         this.dom = dh.append(pel, o);
7324     }
7325     if(config.cls){
7326         this.addClass(config.cls);
7327     }
7328     this.constrain = config.constrain !== false;
7329     this.visibilityMode = Roo.Element.VISIBILITY;
7330     if(config.id){
7331         this.id = this.dom.id = config.id;
7332     }else{
7333         this.id = Roo.id(this.dom);
7334     }
7335     this.zindex = config.zindex || this.getZIndex();
7336     this.position("absolute", this.zindex);
7337     if(config.shadow){
7338         this.shadowOffset = config.shadowOffset || 4;
7339         this.shadow = new Roo.Shadow({
7340             offset : this.shadowOffset,
7341             mode : config.shadow
7342         });
7343     }else{
7344         this.shadowOffset = 0;
7345     }
7346     this.useShim = config.shim !== false && Roo.useShims;
7347     this.useDisplay = config.useDisplay;
7348     this.hide();
7349 };
7350
7351 var supr = Roo.Element.prototype;
7352
7353 // shims are shared among layer to keep from having 100 iframes
7354 var shims = [];
7355
7356 Roo.extend(Roo.Layer, Roo.Element, {
7357
7358     getZIndex : function(){
7359         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7360     },
7361
7362     getShim : function(){
7363         if(!this.useShim){
7364             return null;
7365         }
7366         if(this.shim){
7367             return this.shim;
7368         }
7369         var shim = shims.shift();
7370         if(!shim){
7371             shim = this.createShim();
7372             shim.enableDisplayMode('block');
7373             shim.dom.style.display = 'none';
7374             shim.dom.style.visibility = 'visible';
7375         }
7376         var pn = this.dom.parentNode;
7377         if(shim.dom.parentNode != pn){
7378             pn.insertBefore(shim.dom, this.dom);
7379         }
7380         shim.setStyle('z-index', this.getZIndex()-2);
7381         this.shim = shim;
7382         return shim;
7383     },
7384
7385     hideShim : function(){
7386         if(this.shim){
7387             this.shim.setDisplayed(false);
7388             shims.push(this.shim);
7389             delete this.shim;
7390         }
7391     },
7392
7393     disableShadow : function(){
7394         if(this.shadow){
7395             this.shadowDisabled = true;
7396             this.shadow.hide();
7397             this.lastShadowOffset = this.shadowOffset;
7398             this.shadowOffset = 0;
7399         }
7400     },
7401
7402     enableShadow : function(show){
7403         if(this.shadow){
7404             this.shadowDisabled = false;
7405             this.shadowOffset = this.lastShadowOffset;
7406             delete this.lastShadowOffset;
7407             if(show){
7408                 this.sync(true);
7409             }
7410         }
7411     },
7412
7413     // private
7414     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7415     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7416     sync : function(doShow){
7417         var sw = this.shadow;
7418         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7419             var sh = this.getShim();
7420
7421             var w = this.getWidth(),
7422                 h = this.getHeight();
7423
7424             var l = this.getLeft(true),
7425                 t = this.getTop(true);
7426
7427             if(sw && !this.shadowDisabled){
7428                 if(doShow && !sw.isVisible()){
7429                     sw.show(this);
7430                 }else{
7431                     sw.realign(l, t, w, h);
7432                 }
7433                 if(sh){
7434                     if(doShow){
7435                        sh.show();
7436                     }
7437                     // fit the shim behind the shadow, so it is shimmed too
7438                     var a = sw.adjusts, s = sh.dom.style;
7439                     s.left = (Math.min(l, l+a.l))+"px";
7440                     s.top = (Math.min(t, t+a.t))+"px";
7441                     s.width = (w+a.w)+"px";
7442                     s.height = (h+a.h)+"px";
7443                 }
7444             }else if(sh){
7445                 if(doShow){
7446                    sh.show();
7447                 }
7448                 sh.setSize(w, h);
7449                 sh.setLeftTop(l, t);
7450             }
7451             
7452         }
7453     },
7454
7455     // private
7456     destroy : function(){
7457         this.hideShim();
7458         if(this.shadow){
7459             this.shadow.hide();
7460         }
7461         this.removeAllListeners();
7462         var pn = this.dom.parentNode;
7463         if(pn){
7464             pn.removeChild(this.dom);
7465         }
7466         Roo.Element.uncache(this.id);
7467     },
7468
7469     remove : function(){
7470         this.destroy();
7471     },
7472
7473     // private
7474     beginUpdate : function(){
7475         this.updating = true;
7476     },
7477
7478     // private
7479     endUpdate : function(){
7480         this.updating = false;
7481         this.sync(true);
7482     },
7483
7484     // private
7485     hideUnders : function(negOffset){
7486         if(this.shadow){
7487             this.shadow.hide();
7488         }
7489         this.hideShim();
7490     },
7491
7492     // private
7493     constrainXY : function(){
7494         if(this.constrain){
7495             var vw = Roo.lib.Dom.getViewWidth(),
7496                 vh = Roo.lib.Dom.getViewHeight();
7497             var s = Roo.get(document).getScroll();
7498
7499             var xy = this.getXY();
7500             var x = xy[0], y = xy[1];   
7501             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7502             // only move it if it needs it
7503             var moved = false;
7504             // first validate right/bottom
7505             if((x + w) > vw+s.left){
7506                 x = vw - w - this.shadowOffset;
7507                 moved = true;
7508             }
7509             if((y + h) > vh+s.top){
7510                 y = vh - h - this.shadowOffset;
7511                 moved = true;
7512             }
7513             // then make sure top/left isn't negative
7514             if(x < s.left){
7515                 x = s.left;
7516                 moved = true;
7517             }
7518             if(y < s.top){
7519                 y = s.top;
7520                 moved = true;
7521             }
7522             if(moved){
7523                 if(this.avoidY){
7524                     var ay = this.avoidY;
7525                     if(y <= ay && (y+h) >= ay){
7526                         y = ay-h-5;   
7527                     }
7528                 }
7529                 xy = [x, y];
7530                 this.storeXY(xy);
7531                 supr.setXY.call(this, xy);
7532                 this.sync();
7533             }
7534         }
7535     },
7536
7537     isVisible : function(){
7538         return this.visible;    
7539     },
7540
7541     // private
7542     showAction : function(){
7543         this.visible = true; // track visibility to prevent getStyle calls
7544         if(this.useDisplay === true){
7545             this.setDisplayed("");
7546         }else if(this.lastXY){
7547             supr.setXY.call(this, this.lastXY);
7548         }else if(this.lastLT){
7549             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7550         }
7551     },
7552
7553     // private
7554     hideAction : function(){
7555         this.visible = false;
7556         if(this.useDisplay === true){
7557             this.setDisplayed(false);
7558         }else{
7559             this.setLeftTop(-10000,-10000);
7560         }
7561     },
7562
7563     // overridden Element method
7564     setVisible : function(v, a, d, c, e){
7565         if(v){
7566             this.showAction();
7567         }
7568         if(a && v){
7569             var cb = function(){
7570                 this.sync(true);
7571                 if(c){
7572                     c();
7573                 }
7574             }.createDelegate(this);
7575             supr.setVisible.call(this, true, true, d, cb, e);
7576         }else{
7577             if(!v){
7578                 this.hideUnders(true);
7579             }
7580             var cb = c;
7581             if(a){
7582                 cb = function(){
7583                     this.hideAction();
7584                     if(c){
7585                         c();
7586                     }
7587                 }.createDelegate(this);
7588             }
7589             supr.setVisible.call(this, v, a, d, cb, e);
7590             if(v){
7591                 this.sync(true);
7592             }else if(!a){
7593                 this.hideAction();
7594             }
7595         }
7596     },
7597
7598     storeXY : function(xy){
7599         delete this.lastLT;
7600         this.lastXY = xy;
7601     },
7602
7603     storeLeftTop : function(left, top){
7604         delete this.lastXY;
7605         this.lastLT = [left, top];
7606     },
7607
7608     // private
7609     beforeFx : function(){
7610         this.beforeAction();
7611         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
7612     },
7613
7614     // private
7615     afterFx : function(){
7616         Roo.Layer.superclass.afterFx.apply(this, arguments);
7617         this.sync(this.isVisible());
7618     },
7619
7620     // private
7621     beforeAction : function(){
7622         if(!this.updating && this.shadow){
7623             this.shadow.hide();
7624         }
7625     },
7626
7627     // overridden Element method
7628     setLeft : function(left){
7629         this.storeLeftTop(left, this.getTop(true));
7630         supr.setLeft.apply(this, arguments);
7631         this.sync();
7632     },
7633
7634     setTop : function(top){
7635         this.storeLeftTop(this.getLeft(true), top);
7636         supr.setTop.apply(this, arguments);
7637         this.sync();
7638     },
7639
7640     setLeftTop : function(left, top){
7641         this.storeLeftTop(left, top);
7642         supr.setLeftTop.apply(this, arguments);
7643         this.sync();
7644     },
7645
7646     setXY : function(xy, a, d, c, e){
7647         this.fixDisplay();
7648         this.beforeAction();
7649         this.storeXY(xy);
7650         var cb = this.createCB(c);
7651         supr.setXY.call(this, xy, a, d, cb, e);
7652         if(!a){
7653             cb();
7654         }
7655     },
7656
7657     // private
7658     createCB : function(c){
7659         var el = this;
7660         return function(){
7661             el.constrainXY();
7662             el.sync(true);
7663             if(c){
7664                 c();
7665             }
7666         };
7667     },
7668
7669     // overridden Element method
7670     setX : function(x, a, d, c, e){
7671         this.setXY([x, this.getY()], a, d, c, e);
7672     },
7673
7674     // overridden Element method
7675     setY : function(y, a, d, c, e){
7676         this.setXY([this.getX(), y], a, d, c, e);
7677     },
7678
7679     // overridden Element method
7680     setSize : function(w, h, a, d, c, e){
7681         this.beforeAction();
7682         var cb = this.createCB(c);
7683         supr.setSize.call(this, w, h, a, d, cb, e);
7684         if(!a){
7685             cb();
7686         }
7687     },
7688
7689     // overridden Element method
7690     setWidth : function(w, a, d, c, e){
7691         this.beforeAction();
7692         var cb = this.createCB(c);
7693         supr.setWidth.call(this, w, a, d, cb, e);
7694         if(!a){
7695             cb();
7696         }
7697     },
7698
7699     // overridden Element method
7700     setHeight : function(h, a, d, c, e){
7701         this.beforeAction();
7702         var cb = this.createCB(c);
7703         supr.setHeight.call(this, h, a, d, cb, e);
7704         if(!a){
7705             cb();
7706         }
7707     },
7708
7709     // overridden Element method
7710     setBounds : function(x, y, w, h, a, d, c, e){
7711         this.beforeAction();
7712         var cb = this.createCB(c);
7713         if(!a){
7714             this.storeXY([x, y]);
7715             supr.setXY.call(this, [x, y]);
7716             supr.setSize.call(this, w, h, a, d, cb, e);
7717             cb();
7718         }else{
7719             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
7720         }
7721         return this;
7722     },
7723     
7724     /**
7725      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
7726      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
7727      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
7728      * @param {Number} zindex The new z-index to set
7729      * @return {this} The Layer
7730      */
7731     setZIndex : function(zindex){
7732         this.zindex = zindex;
7733         this.setStyle("z-index", zindex + 2);
7734         if(this.shadow){
7735             this.shadow.setZIndex(zindex + 1);
7736         }
7737         if(this.shim){
7738             this.shim.setStyle("z-index", zindex);
7739         }
7740     }
7741 });
7742 })();/*
7743  * Based on:
7744  * Ext JS Library 1.1.1
7745  * Copyright(c) 2006-2007, Ext JS, LLC.
7746  *
7747  * Originally Released Under LGPL - original licence link has changed is not relivant.
7748  *
7749  * Fork - LGPL
7750  * <script type="text/javascript">
7751  */
7752
7753
7754 /**
7755  * @class Roo.Shadow
7756  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
7757  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
7758  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
7759  * @constructor
7760  * Create a new Shadow
7761  * @param {Object} config The config object
7762  */
7763 Roo.Shadow = function(config){
7764     Roo.apply(this, config);
7765     if(typeof this.mode != "string"){
7766         this.mode = this.defaultMode;
7767     }
7768     var o = this.offset, a = {h: 0};
7769     var rad = Math.floor(this.offset/2);
7770     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
7771         case "drop":
7772             a.w = 0;
7773             a.l = a.t = o;
7774             a.t -= 1;
7775             if(Roo.isIE){
7776                 a.l -= this.offset + rad;
7777                 a.t -= this.offset + rad;
7778                 a.w -= rad;
7779                 a.h -= rad;
7780                 a.t += 1;
7781             }
7782         break;
7783         case "sides":
7784             a.w = (o*2);
7785             a.l = -o;
7786             a.t = o-1;
7787             if(Roo.isIE){
7788                 a.l -= (this.offset - rad);
7789                 a.t -= this.offset + rad;
7790                 a.l += 1;
7791                 a.w -= (this.offset - rad)*2;
7792                 a.w -= rad + 1;
7793                 a.h -= 1;
7794             }
7795         break;
7796         case "frame":
7797             a.w = a.h = (o*2);
7798             a.l = a.t = -o;
7799             a.t += 1;
7800             a.h -= 2;
7801             if(Roo.isIE){
7802                 a.l -= (this.offset - rad);
7803                 a.t -= (this.offset - rad);
7804                 a.l += 1;
7805                 a.w -= (this.offset + rad + 1);
7806                 a.h -= (this.offset + rad);
7807                 a.h += 1;
7808             }
7809         break;
7810     };
7811
7812     this.adjusts = a;
7813 };
7814
7815 Roo.Shadow.prototype = {
7816     /**
7817      * @cfg {String} mode
7818      * The shadow display mode.  Supports the following options:<br />
7819      * sides: Shadow displays on both sides and bottom only<br />
7820      * frame: Shadow displays equally on all four sides<br />
7821      * drop: Traditional bottom-right drop shadow (default)
7822      */
7823     /**
7824      * @cfg {String} offset
7825      * The number of pixels to offset the shadow from the element (defaults to 4)
7826      */
7827     offset: 4,
7828
7829     // private
7830     defaultMode: "drop",
7831
7832     /**
7833      * Displays the shadow under the target element
7834      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
7835      */
7836     show : function(target){
7837         target = Roo.get(target);
7838         if(!this.el){
7839             this.el = Roo.Shadow.Pool.pull();
7840             if(this.el.dom.nextSibling != target.dom){
7841                 this.el.insertBefore(target);
7842             }
7843         }
7844         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
7845         if(Roo.isIE){
7846             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
7847         }
7848         this.realign(
7849             target.getLeft(true),
7850             target.getTop(true),
7851             target.getWidth(),
7852             target.getHeight()
7853         );
7854         this.el.dom.style.display = "block";
7855     },
7856
7857     /**
7858      * Returns true if the shadow is visible, else false
7859      */
7860     isVisible : function(){
7861         return this.el ? true : false;  
7862     },
7863
7864     /**
7865      * Direct alignment when values are already available. Show must be called at least once before
7866      * calling this method to ensure it is initialized.
7867      * @param {Number} left The target element left position
7868      * @param {Number} top The target element top position
7869      * @param {Number} width The target element width
7870      * @param {Number} height The target element height
7871      */
7872     realign : function(l, t, w, h){
7873         if(!this.el){
7874             return;
7875         }
7876         var a = this.adjusts, d = this.el.dom, s = d.style;
7877         var iea = 0;
7878         s.left = (l+a.l)+"px";
7879         s.top = (t+a.t)+"px";
7880         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
7881  
7882         if(s.width != sws || s.height != shs){
7883             s.width = sws;
7884             s.height = shs;
7885             if(!Roo.isIE){
7886                 var cn = d.childNodes;
7887                 var sww = Math.max(0, (sw-12))+"px";
7888                 cn[0].childNodes[1].style.width = sww;
7889                 cn[1].childNodes[1].style.width = sww;
7890                 cn[2].childNodes[1].style.width = sww;
7891                 cn[1].style.height = Math.max(0, (sh-12))+"px";
7892             }
7893         }
7894     },
7895
7896     /**
7897      * Hides this shadow
7898      */
7899     hide : function(){
7900         if(this.el){
7901             this.el.dom.style.display = "none";
7902             Roo.Shadow.Pool.push(this.el);
7903             delete this.el;
7904         }
7905     },
7906
7907     /**
7908      * Adjust the z-index of this shadow
7909      * @param {Number} zindex The new z-index
7910      */
7911     setZIndex : function(z){
7912         this.zIndex = z;
7913         if(this.el){
7914             this.el.setStyle("z-index", z);
7915         }
7916     }
7917 };
7918
7919 // Private utility class that manages the internal Shadow cache
7920 Roo.Shadow.Pool = function(){
7921     var p = [];
7922     var markup = Roo.isIE ?
7923                  '<div class="x-ie-shadow"></div>' :
7924                  '<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>';
7925     return {
7926         pull : function(){
7927             var sh = p.shift();
7928             if(!sh){
7929                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
7930                 sh.autoBoxAdjust = false;
7931             }
7932             return sh;
7933         },
7934
7935         push : function(sh){
7936             p.push(sh);
7937         }
7938     };
7939 }();/*
7940  * Based on:
7941  * Ext JS Library 1.1.1
7942  * Copyright(c) 2006-2007, Ext JS, LLC.
7943  *
7944  * Originally Released Under LGPL - original licence link has changed is not relivant.
7945  *
7946  * Fork - LGPL
7947  * <script type="text/javascript">
7948  */
7949
7950
7951 /**
7952  * @class Roo.SplitBar
7953  * @extends Roo.util.Observable
7954  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
7955  * <br><br>
7956  * Usage:
7957  * <pre><code>
7958 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
7959                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
7960 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
7961 split.minSize = 100;
7962 split.maxSize = 600;
7963 split.animate = true;
7964 split.on('moved', splitterMoved);
7965 </code></pre>
7966  * @constructor
7967  * Create a new SplitBar
7968  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
7969  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
7970  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
7971  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
7972                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
7973                         position of the SplitBar).
7974  */
7975 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
7976     
7977     /** @private */
7978     this.el = Roo.get(dragElement, true);
7979     this.el.dom.unselectable = "on";
7980     /** @private */
7981     this.resizingEl = Roo.get(resizingElement, true);
7982
7983     /**
7984      * @private
7985      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
7986      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
7987      * @type Number
7988      */
7989     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
7990     
7991     /**
7992      * The minimum size of the resizing element. (Defaults to 0)
7993      * @type Number
7994      */
7995     this.minSize = 0;
7996     
7997     /**
7998      * The maximum size of the resizing element. (Defaults to 2000)
7999      * @type Number
8000      */
8001     this.maxSize = 2000;
8002     
8003     /**
8004      * Whether to animate the transition to the new size
8005      * @type Boolean
8006      */
8007     this.animate = false;
8008     
8009     /**
8010      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8011      * @type Boolean
8012      */
8013     this.useShim = false;
8014     
8015     /** @private */
8016     this.shim = null;
8017     
8018     if(!existingProxy){
8019         /** @private */
8020         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8021     }else{
8022         this.proxy = Roo.get(existingProxy).dom;
8023     }
8024     /** @private */
8025     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8026     
8027     /** @private */
8028     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8029     
8030     /** @private */
8031     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8032     
8033     /** @private */
8034     this.dragSpecs = {};
8035     
8036     /**
8037      * @private The adapter to use to positon and resize elements
8038      */
8039     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8040     this.adapter.init(this);
8041     
8042     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8043         /** @private */
8044         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8045         this.el.addClass("x-splitbar-h");
8046     }else{
8047         /** @private */
8048         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8049         this.el.addClass("x-splitbar-v");
8050     }
8051     
8052     this.addEvents({
8053         /**
8054          * @event resize
8055          * Fires when the splitter is moved (alias for {@link #event-moved})
8056          * @param {Roo.SplitBar} this
8057          * @param {Number} newSize the new width or height
8058          */
8059         "resize" : true,
8060         /**
8061          * @event moved
8062          * Fires when the splitter is moved
8063          * @param {Roo.SplitBar} this
8064          * @param {Number} newSize the new width or height
8065          */
8066         "moved" : true,
8067         /**
8068          * @event beforeresize
8069          * Fires before the splitter is dragged
8070          * @param {Roo.SplitBar} this
8071          */
8072         "beforeresize" : true,
8073
8074         "beforeapply" : true
8075     });
8076
8077     Roo.util.Observable.call(this);
8078 };
8079
8080 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8081     onStartProxyDrag : function(x, y){
8082         this.fireEvent("beforeresize", this);
8083         if(!this.overlay){
8084             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8085             o.unselectable();
8086             o.enableDisplayMode("block");
8087             // all splitbars share the same overlay
8088             Roo.SplitBar.prototype.overlay = o;
8089         }
8090         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8091         this.overlay.show();
8092         Roo.get(this.proxy).setDisplayed("block");
8093         var size = this.adapter.getElementSize(this);
8094         this.activeMinSize = this.getMinimumSize();;
8095         this.activeMaxSize = this.getMaximumSize();;
8096         var c1 = size - this.activeMinSize;
8097         var c2 = Math.max(this.activeMaxSize - size, 0);
8098         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8099             this.dd.resetConstraints();
8100             this.dd.setXConstraint(
8101                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8102                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8103             );
8104             this.dd.setYConstraint(0, 0);
8105         }else{
8106             this.dd.resetConstraints();
8107             this.dd.setXConstraint(0, 0);
8108             this.dd.setYConstraint(
8109                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8110                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8111             );
8112          }
8113         this.dragSpecs.startSize = size;
8114         this.dragSpecs.startPoint = [x, y];
8115         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8116     },
8117     
8118     /** 
8119      * @private Called after the drag operation by the DDProxy
8120      */
8121     onEndProxyDrag : function(e){
8122         Roo.get(this.proxy).setDisplayed(false);
8123         var endPoint = Roo.lib.Event.getXY(e);
8124         if(this.overlay){
8125             this.overlay.hide();
8126         }
8127         var newSize;
8128         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8129             newSize = this.dragSpecs.startSize + 
8130                 (this.placement == Roo.SplitBar.LEFT ?
8131                     endPoint[0] - this.dragSpecs.startPoint[0] :
8132                     this.dragSpecs.startPoint[0] - endPoint[0]
8133                 );
8134         }else{
8135             newSize = this.dragSpecs.startSize + 
8136                 (this.placement == Roo.SplitBar.TOP ?
8137                     endPoint[1] - this.dragSpecs.startPoint[1] :
8138                     this.dragSpecs.startPoint[1] - endPoint[1]
8139                 );
8140         }
8141         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8142         if(newSize != this.dragSpecs.startSize){
8143             if(this.fireEvent('beforeapply', this, newSize) !== false){
8144                 this.adapter.setElementSize(this, newSize);
8145                 this.fireEvent("moved", this, newSize);
8146                 this.fireEvent("resize", this, newSize);
8147             }
8148         }
8149     },
8150     
8151     /**
8152      * Get the adapter this SplitBar uses
8153      * @return The adapter object
8154      */
8155     getAdapter : function(){
8156         return this.adapter;
8157     },
8158     
8159     /**
8160      * Set the adapter this SplitBar uses
8161      * @param {Object} adapter A SplitBar adapter object
8162      */
8163     setAdapter : function(adapter){
8164         this.adapter = adapter;
8165         this.adapter.init(this);
8166     },
8167     
8168     /**
8169      * Gets the minimum size for the resizing element
8170      * @return {Number} The minimum size
8171      */
8172     getMinimumSize : function(){
8173         return this.minSize;
8174     },
8175     
8176     /**
8177      * Sets the minimum size for the resizing element
8178      * @param {Number} minSize The minimum size
8179      */
8180     setMinimumSize : function(minSize){
8181         this.minSize = minSize;
8182     },
8183     
8184     /**
8185      * Gets the maximum size for the resizing element
8186      * @return {Number} The maximum size
8187      */
8188     getMaximumSize : function(){
8189         return this.maxSize;
8190     },
8191     
8192     /**
8193      * Sets the maximum size for the resizing element
8194      * @param {Number} maxSize The maximum size
8195      */
8196     setMaximumSize : function(maxSize){
8197         this.maxSize = maxSize;
8198     },
8199     
8200     /**
8201      * Sets the initialize size for the resizing element
8202      * @param {Number} size The initial size
8203      */
8204     setCurrentSize : function(size){
8205         var oldAnimate = this.animate;
8206         this.animate = false;
8207         this.adapter.setElementSize(this, size);
8208         this.animate = oldAnimate;
8209     },
8210     
8211     /**
8212      * Destroy this splitbar. 
8213      * @param {Boolean} removeEl True to remove the element
8214      */
8215     destroy : function(removeEl){
8216         if(this.shim){
8217             this.shim.remove();
8218         }
8219         this.dd.unreg();
8220         this.proxy.parentNode.removeChild(this.proxy);
8221         if(removeEl){
8222             this.el.remove();
8223         }
8224     }
8225 });
8226
8227 /**
8228  * @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.
8229  */
8230 Roo.SplitBar.createProxy = function(dir){
8231     var proxy = new Roo.Element(document.createElement("div"));
8232     proxy.unselectable();
8233     var cls = 'x-splitbar-proxy';
8234     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8235     document.body.appendChild(proxy.dom);
8236     return proxy.dom;
8237 };
8238
8239 /** 
8240  * @class Roo.SplitBar.BasicLayoutAdapter
8241  * Default Adapter. It assumes the splitter and resizing element are not positioned
8242  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8243  */
8244 Roo.SplitBar.BasicLayoutAdapter = function(){
8245 };
8246
8247 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8248     // do nothing for now
8249     init : function(s){
8250     
8251     },
8252     /**
8253      * Called before drag operations to get the current size of the resizing element. 
8254      * @param {Roo.SplitBar} s The SplitBar using this adapter
8255      */
8256      getElementSize : function(s){
8257         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8258             return s.resizingEl.getWidth();
8259         }else{
8260             return s.resizingEl.getHeight();
8261         }
8262     },
8263     
8264     /**
8265      * Called after drag operations to set the size of the resizing element.
8266      * @param {Roo.SplitBar} s The SplitBar using this adapter
8267      * @param {Number} newSize The new size to set
8268      * @param {Function} onComplete A function to be invoked when resizing is complete
8269      */
8270     setElementSize : function(s, newSize, onComplete){
8271         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8272             if(!s.animate){
8273                 s.resizingEl.setWidth(newSize);
8274                 if(onComplete){
8275                     onComplete(s, newSize);
8276                 }
8277             }else{
8278                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8279             }
8280         }else{
8281             
8282             if(!s.animate){
8283                 s.resizingEl.setHeight(newSize);
8284                 if(onComplete){
8285                     onComplete(s, newSize);
8286                 }
8287             }else{
8288                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8289             }
8290         }
8291     }
8292 };
8293
8294 /** 
8295  *@class Roo.SplitBar.AbsoluteLayoutAdapter
8296  * @extends Roo.SplitBar.BasicLayoutAdapter
8297  * Adapter that  moves the splitter element to align with the resized sizing element. 
8298  * Used with an absolute positioned SplitBar.
8299  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
8300  * document.body, make sure you assign an id to the body element.
8301  */
8302 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
8303     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
8304     this.container = Roo.get(container);
8305 };
8306
8307 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
8308     init : function(s){
8309         this.basic.init(s);
8310     },
8311     
8312     getElementSize : function(s){
8313         return this.basic.getElementSize(s);
8314     },
8315     
8316     setElementSize : function(s, newSize, onComplete){
8317         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
8318     },
8319     
8320     moveSplitter : function(s){
8321         var yes = Roo.SplitBar;
8322         switch(s.placement){
8323             case yes.LEFT:
8324                 s.el.setX(s.resizingEl.getRight());
8325                 break;
8326             case yes.RIGHT:
8327                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
8328                 break;
8329             case yes.TOP:
8330                 s.el.setY(s.resizingEl.getBottom());
8331                 break;
8332             case yes.BOTTOM:
8333                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
8334                 break;
8335         }
8336     }
8337 };
8338
8339 /**
8340  * Orientation constant - Create a vertical SplitBar
8341  * @static
8342  * @type Number
8343  */
8344 Roo.SplitBar.VERTICAL = 1;
8345
8346 /**
8347  * Orientation constant - Create a horizontal SplitBar
8348  * @static
8349  * @type Number
8350  */
8351 Roo.SplitBar.HORIZONTAL = 2;
8352
8353 /**
8354  * Placement constant - The resizing element is to the left of the splitter element
8355  * @static
8356  * @type Number
8357  */
8358 Roo.SplitBar.LEFT = 1;
8359
8360 /**
8361  * Placement constant - The resizing element is to the right of the splitter element
8362  * @static
8363  * @type Number
8364  */
8365 Roo.SplitBar.RIGHT = 2;
8366
8367 /**
8368  * Placement constant - The resizing element is positioned above the splitter element
8369  * @static
8370  * @type Number
8371  */
8372 Roo.SplitBar.TOP = 3;
8373
8374 /**
8375  * Placement constant - The resizing element is positioned under splitter element
8376  * @static
8377  * @type Number
8378  */
8379 Roo.SplitBar.BOTTOM = 4;
8380 /*
8381  * Based on:
8382  * Ext JS Library 1.1.1
8383  * Copyright(c) 2006-2007, Ext JS, LLC.
8384  *
8385  * Originally Released Under LGPL - original licence link has changed is not relivant.
8386  *
8387  * Fork - LGPL
8388  * <script type="text/javascript">
8389  */
8390
8391 /**
8392  * @class Roo.View
8393  * @extends Roo.util.Observable
8394  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
8395  * This class also supports single and multi selection modes. <br>
8396  * Create a data model bound view:
8397  <pre><code>
8398  var store = new Roo.data.Store(...);
8399
8400  var view = new Roo.View({
8401     el : "my-element",
8402     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
8403  
8404     singleSelect: true,
8405     selectedClass: "ydataview-selected",
8406     store: store
8407  });
8408
8409  // listen for node click?
8410  view.on("click", function(vw, index, node, e){
8411  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
8412  });
8413
8414  // load XML data
8415  dataModel.load("foobar.xml");
8416  </code></pre>
8417  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
8418  * <br><br>
8419  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
8420  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
8421  * 
8422  * Note: old style constructor is still suported (container, template, config)
8423  * 
8424  * @constructor
8425  * Create a new View
8426  * @param {Object} config The config object
8427  * 
8428  */
8429 Roo.View = function(config, depreciated_tpl, depreciated_config){
8430     
8431     this.parent = false;
8432     
8433     if (typeof(depreciated_tpl) == 'undefined') {
8434         // new way.. - universal constructor.
8435         Roo.apply(this, config);
8436         this.el  = Roo.get(this.el);
8437     } else {
8438         // old format..
8439         this.el  = Roo.get(config);
8440         this.tpl = depreciated_tpl;
8441         Roo.apply(this, depreciated_config);
8442     }
8443     this.wrapEl  = this.el.wrap().wrap();
8444     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
8445     
8446     
8447     if(typeof(this.tpl) == "string"){
8448         this.tpl = new Roo.Template(this.tpl);
8449     } else {
8450         // support xtype ctors..
8451         this.tpl = new Roo.factory(this.tpl, Roo);
8452     }
8453     
8454     
8455     this.tpl.compile();
8456     
8457     /** @private */
8458     this.addEvents({
8459         /**
8460          * @event beforeclick
8461          * Fires before a click is processed. Returns false to cancel the default action.
8462          * @param {Roo.View} this
8463          * @param {Number} index The index of the target node
8464          * @param {HTMLElement} node The target node
8465          * @param {Roo.EventObject} e The raw event object
8466          */
8467             "beforeclick" : true,
8468         /**
8469          * @event click
8470          * Fires when a template node is clicked.
8471          * @param {Roo.View} this
8472          * @param {Number} index The index of the target node
8473          * @param {HTMLElement} node The target node
8474          * @param {Roo.EventObject} e The raw event object
8475          */
8476             "click" : true,
8477         /**
8478          * @event dblclick
8479          * Fires when a template node is double clicked.
8480          * @param {Roo.View} this
8481          * @param {Number} index The index of the target node
8482          * @param {HTMLElement} node The target node
8483          * @param {Roo.EventObject} e The raw event object
8484          */
8485             "dblclick" : true,
8486         /**
8487          * @event contextmenu
8488          * Fires when a template node is right clicked.
8489          * @param {Roo.View} this
8490          * @param {Number} index The index of the target node
8491          * @param {HTMLElement} node The target node
8492          * @param {Roo.EventObject} e The raw event object
8493          */
8494             "contextmenu" : true,
8495         /**
8496          * @event selectionchange
8497          * Fires when the selected nodes change.
8498          * @param {Roo.View} this
8499          * @param {Array} selections Array of the selected nodes
8500          */
8501             "selectionchange" : true,
8502     
8503         /**
8504          * @event beforeselect
8505          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
8506          * @param {Roo.View} this
8507          * @param {HTMLElement} node The node to be selected
8508          * @param {Array} selections Array of currently selected nodes
8509          */
8510             "beforeselect" : true,
8511         /**
8512          * @event preparedata
8513          * Fires on every row to render, to allow you to change the data.
8514          * @param {Roo.View} this
8515          * @param {Object} data to be rendered (change this)
8516          */
8517           "preparedata" : true
8518           
8519           
8520         });
8521
8522
8523
8524     this.el.on({
8525         "click": this.onClick,
8526         "dblclick": this.onDblClick,
8527         "contextmenu": this.onContextMenu,
8528         scope:this
8529     });
8530
8531     this.selections = [];
8532     this.nodes = [];
8533     this.cmp = new Roo.CompositeElementLite([]);
8534     if(this.store){
8535         this.store = Roo.factory(this.store, Roo.data);
8536         this.setStore(this.store, true);
8537     }
8538     
8539     if ( this.footer && this.footer.xtype) {
8540            
8541          var fctr = this.wrapEl.appendChild(document.createElement("div"));
8542         
8543         this.footer.dataSource = this.store
8544         this.footer.container = fctr;
8545         this.footer = Roo.factory(this.footer, Roo);
8546         fctr.insertFirst(this.el);
8547         
8548         // this is a bit insane - as the paging toolbar seems to detach the el..
8549 //        dom.parentNode.parentNode.parentNode
8550          // they get detached?
8551     }
8552     
8553     
8554     Roo.View.superclass.constructor.call(this);
8555     
8556     
8557 };
8558
8559 Roo.extend(Roo.View, Roo.util.Observable, {
8560     
8561      /**
8562      * @cfg {Roo.data.Store} store Data store to load data from.
8563      */
8564     store : false,
8565     
8566     /**
8567      * @cfg {String|Roo.Element} el The container element.
8568      */
8569     el : '',
8570     
8571     /**
8572      * @cfg {String|Roo.Template} tpl The template used by this View 
8573      */
8574     tpl : false,
8575     /**
8576      * @cfg {String} dataName the named area of the template to use as the data area
8577      *                          Works with domtemplates roo-name="name"
8578      */
8579     dataName: false,
8580     /**
8581      * @cfg {String} selectedClass The css class to add to selected nodes
8582      */
8583     selectedClass : "x-view-selected",
8584      /**
8585      * @cfg {String} emptyText The empty text to show when nothing is loaded.
8586      */
8587     emptyText : "",
8588     
8589     /**
8590      * @cfg {String} text to display on mask (default Loading)
8591      */
8592     mask : false,
8593     /**
8594      * @cfg {Boolean} multiSelect Allow multiple selection
8595      */
8596     multiSelect : false,
8597     /**
8598      * @cfg {Boolean} singleSelect Allow single selection
8599      */
8600     singleSelect:  false,
8601     
8602     /**
8603      * @cfg {Boolean} toggleSelect - selecting 
8604      */
8605     toggleSelect : false,
8606     
8607     /**
8608      * @cfg {Boolean} tickable - selecting 
8609      */
8610     tickable : false,
8611     
8612     /**
8613      * Returns the element this view is bound to.
8614      * @return {Roo.Element}
8615      */
8616     getEl : function(){
8617         return this.wrapEl;
8618     },
8619     
8620     
8621
8622     /**
8623      * Refreshes the view. - called by datachanged on the store. - do not call directly.
8624      */
8625     refresh : function(){
8626         Roo.log('refresh');
8627         var t = this.tpl;
8628         
8629         // if we are using something like 'domtemplate', then
8630         // the what gets used is:
8631         // t.applySubtemplate(NAME, data, wrapping data..)
8632         // the outer template then get' applied with
8633         //     the store 'extra data'
8634         // and the body get's added to the
8635         //      roo-name="data" node?
8636         //      <span class='roo-tpl-{name}'></span> ?????
8637         
8638         
8639         
8640         this.clearSelections();
8641         this.el.update("");
8642         var html = [];
8643         var records = this.store.getRange();
8644         if(records.length < 1) {
8645             
8646             // is this valid??  = should it render a template??
8647             
8648             this.el.update(this.emptyText);
8649             return;
8650         }
8651         var el = this.el;
8652         if (this.dataName) {
8653             this.el.update(t.apply(this.store.meta)); //????
8654             el = this.el.child('.roo-tpl-' + this.dataName);
8655         }
8656         
8657         for(var i = 0, len = records.length; i < len; i++){
8658             var data = this.prepareData(records[i].data, i, records[i]);
8659             this.fireEvent("preparedata", this, data, i, records[i]);
8660             
8661             var d = Roo.apply({}, data);
8662             
8663             if(this.tickable){
8664                 Roo.apply(d, {'roo-id' : Roo.id()});
8665                 
8666                 var _this = this;
8667             
8668                 Roo.each(this.parent.item, function(item){
8669                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
8670                         return;
8671                     }
8672                     Roo.apply(d, {'roo-data-checked' : 'checked'});
8673                 });
8674             }
8675             
8676             html[html.length] = Roo.util.Format.trim(
8677                 this.dataName ?
8678                     t.applySubtemplate(this.dataName, d, this.store.meta) :
8679                     t.apply(d)
8680             );
8681         }
8682         
8683         
8684         
8685         el.update(html.join(""));
8686         this.nodes = el.dom.childNodes;
8687         this.updateIndexes(0);
8688     },
8689     
8690
8691     /**
8692      * Function to override to reformat the data that is sent to
8693      * the template for each node.
8694      * DEPRICATED - use the preparedata event handler.
8695      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
8696      * a JSON object for an UpdateManager bound view).
8697      */
8698     prepareData : function(data, index, record)
8699     {
8700         this.fireEvent("preparedata", this, data, index, record);
8701         return data;
8702     },
8703
8704     onUpdate : function(ds, record){
8705          Roo.log('on update');   
8706         this.clearSelections();
8707         var index = this.store.indexOf(record);
8708         var n = this.nodes[index];
8709         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
8710         n.parentNode.removeChild(n);
8711         this.updateIndexes(index, index);
8712     },
8713
8714     
8715     
8716 // --------- FIXME     
8717     onAdd : function(ds, records, index)
8718     {
8719         Roo.log(['on Add', ds, records, index] );        
8720         this.clearSelections();
8721         if(this.nodes.length == 0){
8722             this.refresh();
8723             return;
8724         }
8725         var n = this.nodes[index];
8726         for(var i = 0, len = records.length; i < len; i++){
8727             var d = this.prepareData(records[i].data, i, records[i]);
8728             if(n){
8729                 this.tpl.insertBefore(n, d);
8730             }else{
8731                 
8732                 this.tpl.append(this.el, d);
8733             }
8734         }
8735         this.updateIndexes(index);
8736     },
8737
8738     onRemove : function(ds, record, index){
8739         Roo.log('onRemove');
8740         this.clearSelections();
8741         var el = this.dataName  ?
8742             this.el.child('.roo-tpl-' + this.dataName) :
8743             this.el; 
8744         
8745         el.dom.removeChild(this.nodes[index]);
8746         this.updateIndexes(index);
8747     },
8748
8749     /**
8750      * Refresh an individual node.
8751      * @param {Number} index
8752      */
8753     refreshNode : function(index){
8754         this.onUpdate(this.store, this.store.getAt(index));
8755     },
8756
8757     updateIndexes : function(startIndex, endIndex){
8758         var ns = this.nodes;
8759         startIndex = startIndex || 0;
8760         endIndex = endIndex || ns.length - 1;
8761         for(var i = startIndex; i <= endIndex; i++){
8762             ns[i].nodeIndex = i;
8763         }
8764     },
8765
8766     /**
8767      * Changes the data store this view uses and refresh the view.
8768      * @param {Store} store
8769      */
8770     setStore : function(store, initial){
8771         if(!initial && this.store){
8772             this.store.un("datachanged", this.refresh);
8773             this.store.un("add", this.onAdd);
8774             this.store.un("remove", this.onRemove);
8775             this.store.un("update", this.onUpdate);
8776             this.store.un("clear", this.refresh);
8777             this.store.un("beforeload", this.onBeforeLoad);
8778             this.store.un("load", this.onLoad);
8779             this.store.un("loadexception", this.onLoad);
8780         }
8781         if(store){
8782           
8783             store.on("datachanged", this.refresh, this);
8784             store.on("add", this.onAdd, this);
8785             store.on("remove", this.onRemove, this);
8786             store.on("update", this.onUpdate, this);
8787             store.on("clear", this.refresh, this);
8788             store.on("beforeload", this.onBeforeLoad, this);
8789             store.on("load", this.onLoad, this);
8790             store.on("loadexception", this.onLoad, this);
8791         }
8792         
8793         if(store){
8794             this.refresh();
8795         }
8796     },
8797     /**
8798      * onbeforeLoad - masks the loading area.
8799      *
8800      */
8801     onBeforeLoad : function(store,opts)
8802     {
8803          Roo.log('onBeforeLoad');   
8804         if (!opts.add) {
8805             this.el.update("");
8806         }
8807         this.el.mask(this.mask ? this.mask : "Loading" ); 
8808     },
8809     onLoad : function ()
8810     {
8811         this.el.unmask();
8812     },
8813     
8814
8815     /**
8816      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
8817      * @param {HTMLElement} node
8818      * @return {HTMLElement} The template node
8819      */
8820     findItemFromChild : function(node){
8821         var el = this.dataName  ?
8822             this.el.child('.roo-tpl-' + this.dataName,true) :
8823             this.el.dom; 
8824         
8825         if(!node || node.parentNode == el){
8826                     return node;
8827             }
8828             var p = node.parentNode;
8829             while(p && p != el){
8830             if(p.parentNode == el){
8831                 return p;
8832             }
8833             p = p.parentNode;
8834         }
8835             return null;
8836     },
8837
8838     /** @ignore */
8839     onClick : function(e){
8840         var item = this.findItemFromChild(e.getTarget());
8841         if(item){
8842             var index = this.indexOf(item);
8843             if(this.onItemClick(item, index, e) !== false){
8844                 this.fireEvent("click", this, index, item, e);
8845             }
8846         }else{
8847             this.clearSelections();
8848         }
8849     },
8850
8851     /** @ignore */
8852     onContextMenu : function(e){
8853         var item = this.findItemFromChild(e.getTarget());
8854         if(item){
8855             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
8856         }
8857     },
8858
8859     /** @ignore */
8860     onDblClick : function(e){
8861         var item = this.findItemFromChild(e.getTarget());
8862         if(item){
8863             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
8864         }
8865     },
8866
8867     onItemClick : function(item, index, e)
8868     {
8869         if(this.fireEvent("beforeclick", this, index, item, e) === false){
8870             return false;
8871         }
8872         if (this.toggleSelect) {
8873             var m = this.isSelected(item) ? 'unselect' : 'select';
8874             Roo.log(m);
8875             var _t = this;
8876             _t[m](item, true, false);
8877             return true;
8878         }
8879         if(this.multiSelect || this.singleSelect){
8880             if(this.multiSelect && e.shiftKey && this.lastSelection){
8881                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
8882             }else{
8883                 this.select(item, this.multiSelect && e.ctrlKey);
8884                 this.lastSelection = item;
8885             }
8886             
8887             if(!this.tickable){
8888                 e.preventDefault();
8889             }
8890             
8891         }
8892         return true;
8893     },
8894
8895     /**
8896      * Get the number of selected nodes.
8897      * @return {Number}
8898      */
8899     getSelectionCount : function(){
8900         return this.selections.length;
8901     },
8902
8903     /**
8904      * Get the currently selected nodes.
8905      * @return {Array} An array of HTMLElements
8906      */
8907     getSelectedNodes : function(){
8908         return this.selections;
8909     },
8910
8911     /**
8912      * Get the indexes of the selected nodes.
8913      * @return {Array}
8914      */
8915     getSelectedIndexes : function(){
8916         var indexes = [], s = this.selections;
8917         for(var i = 0, len = s.length; i < len; i++){
8918             indexes.push(s[i].nodeIndex);
8919         }
8920         return indexes;
8921     },
8922
8923     /**
8924      * Clear all selections
8925      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
8926      */
8927     clearSelections : function(suppressEvent){
8928         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
8929             this.cmp.elements = this.selections;
8930             this.cmp.removeClass(this.selectedClass);
8931             this.selections = [];
8932             if(!suppressEvent){
8933                 this.fireEvent("selectionchange", this, this.selections);
8934             }
8935         }
8936     },
8937
8938     /**
8939      * Returns true if the passed node is selected
8940      * @param {HTMLElement/Number} node The node or node index
8941      * @return {Boolean}
8942      */
8943     isSelected : function(node){
8944         var s = this.selections;
8945         if(s.length < 1){
8946             return false;
8947         }
8948         node = this.getNode(node);
8949         return s.indexOf(node) !== -1;
8950     },
8951
8952     /**
8953      * Selects nodes.
8954      * @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
8955      * @param {Boolean} keepExisting (optional) true to keep existing selections
8956      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
8957      */
8958     select : function(nodeInfo, keepExisting, suppressEvent){
8959         if(nodeInfo instanceof Array){
8960             if(!keepExisting){
8961                 this.clearSelections(true);
8962             }
8963             for(var i = 0, len = nodeInfo.length; i < len; i++){
8964                 this.select(nodeInfo[i], true, true);
8965             }
8966             return;
8967         } 
8968         var node = this.getNode(nodeInfo);
8969         if(!node || this.isSelected(node)){
8970             return; // already selected.
8971         }
8972         if(!keepExisting){
8973             this.clearSelections(true);
8974         }
8975         
8976         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
8977             Roo.fly(node).addClass(this.selectedClass);
8978             this.selections.push(node);
8979             if(!suppressEvent){
8980                 this.fireEvent("selectionchange", this, this.selections);
8981             }
8982         }
8983         
8984         
8985     },
8986       /**
8987      * Unselects nodes.
8988      * @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
8989      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
8990      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
8991      */
8992     unselect : function(nodeInfo, keepExisting, suppressEvent)
8993     {
8994         if(nodeInfo instanceof Array){
8995             Roo.each(this.selections, function(s) {
8996                 this.unselect(s, nodeInfo);
8997             }, this);
8998             return;
8999         }
9000         var node = this.getNode(nodeInfo);
9001         if(!node || !this.isSelected(node)){
9002             Roo.log("not selected");
9003             return; // not selected.
9004         }
9005         // fireevent???
9006         var ns = [];
9007         Roo.each(this.selections, function(s) {
9008             if (s == node ) {
9009                 Roo.fly(node).removeClass(this.selectedClass);
9010
9011                 return;
9012             }
9013             ns.push(s);
9014         },this);
9015         
9016         this.selections= ns;
9017         this.fireEvent("selectionchange", this, this.selections);
9018     },
9019
9020     /**
9021      * Gets a template node.
9022      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9023      * @return {HTMLElement} The node or null if it wasn't found
9024      */
9025     getNode : function(nodeInfo){
9026         if(typeof nodeInfo == "string"){
9027             return document.getElementById(nodeInfo);
9028         }else if(typeof nodeInfo == "number"){
9029             return this.nodes[nodeInfo];
9030         }
9031         return nodeInfo;
9032     },
9033
9034     /**
9035      * Gets a range template nodes.
9036      * @param {Number} startIndex
9037      * @param {Number} endIndex
9038      * @return {Array} An array of nodes
9039      */
9040     getNodes : function(start, end){
9041         var ns = this.nodes;
9042         start = start || 0;
9043         end = typeof end == "undefined" ? ns.length - 1 : end;
9044         var nodes = [];
9045         if(start <= end){
9046             for(var i = start; i <= end; i++){
9047                 nodes.push(ns[i]);
9048             }
9049         } else{
9050             for(var i = start; i >= end; i--){
9051                 nodes.push(ns[i]);
9052             }
9053         }
9054         return nodes;
9055     },
9056
9057     /**
9058      * Finds the index of the passed node
9059      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9060      * @return {Number} The index of the node or -1
9061      */
9062     indexOf : function(node){
9063         node = this.getNode(node);
9064         if(typeof node.nodeIndex == "number"){
9065             return node.nodeIndex;
9066         }
9067         var ns = this.nodes;
9068         for(var i = 0, len = ns.length; i < len; i++){
9069             if(ns[i] == node){
9070                 return i;
9071             }
9072         }
9073         return -1;
9074     }
9075 });
9076 /*
9077  * Based on:
9078  * Ext JS Library 1.1.1
9079  * Copyright(c) 2006-2007, Ext JS, LLC.
9080  *
9081  * Originally Released Under LGPL - original licence link has changed is not relivant.
9082  *
9083  * Fork - LGPL
9084  * <script type="text/javascript">
9085  */
9086
9087 /**
9088  * @class Roo.JsonView
9089  * @extends Roo.View
9090  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9091 <pre><code>
9092 var view = new Roo.JsonView({
9093     container: "my-element",
9094     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9095     multiSelect: true, 
9096     jsonRoot: "data" 
9097 });
9098
9099 // listen for node click?
9100 view.on("click", function(vw, index, node, e){
9101     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9102 });
9103
9104 // direct load of JSON data
9105 view.load("foobar.php");
9106
9107 // Example from my blog list
9108 var tpl = new Roo.Template(
9109     '&lt;div class="entry"&gt;' +
9110     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9111     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9112     "&lt;/div&gt;&lt;hr /&gt;"
9113 );
9114
9115 var moreView = new Roo.JsonView({
9116     container :  "entry-list", 
9117     template : tpl,
9118     jsonRoot: "posts"
9119 });
9120 moreView.on("beforerender", this.sortEntries, this);
9121 moreView.load({
9122     url: "/blog/get-posts.php",
9123     params: "allposts=true",
9124     text: "Loading Blog Entries..."
9125 });
9126 </code></pre>
9127
9128 * Note: old code is supported with arguments : (container, template, config)
9129
9130
9131  * @constructor
9132  * Create a new JsonView
9133  * 
9134  * @param {Object} config The config object
9135  * 
9136  */
9137 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9138     
9139     
9140     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9141
9142     var um = this.el.getUpdateManager();
9143     um.setRenderer(this);
9144     um.on("update", this.onLoad, this);
9145     um.on("failure", this.onLoadException, this);
9146
9147     /**
9148      * @event beforerender
9149      * Fires before rendering of the downloaded JSON data.
9150      * @param {Roo.JsonView} this
9151      * @param {Object} data The JSON data loaded
9152      */
9153     /**
9154      * @event load
9155      * Fires when data is loaded.
9156      * @param {Roo.JsonView} this
9157      * @param {Object} data The JSON data loaded
9158      * @param {Object} response The raw Connect response object
9159      */
9160     /**
9161      * @event loadexception
9162      * Fires when loading fails.
9163      * @param {Roo.JsonView} this
9164      * @param {Object} response The raw Connect response object
9165      */
9166     this.addEvents({
9167         'beforerender' : true,
9168         'load' : true,
9169         'loadexception' : true
9170     });
9171 };
9172 Roo.extend(Roo.JsonView, Roo.View, {
9173     /**
9174      * @type {String} The root property in the loaded JSON object that contains the data
9175      */
9176     jsonRoot : "",
9177
9178     /**
9179      * Refreshes the view.
9180      */
9181     refresh : function(){
9182         this.clearSelections();
9183         this.el.update("");
9184         var html = [];
9185         var o = this.jsonData;
9186         if(o && o.length > 0){
9187             for(var i = 0, len = o.length; i < len; i++){
9188                 var data = this.prepareData(o[i], i, o);
9189                 html[html.length] = this.tpl.apply(data);
9190             }
9191         }else{
9192             html.push(this.emptyText);
9193         }
9194         this.el.update(html.join(""));
9195         this.nodes = this.el.dom.childNodes;
9196         this.updateIndexes(0);
9197     },
9198
9199     /**
9200      * 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.
9201      * @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:
9202      <pre><code>
9203      view.load({
9204          url: "your-url.php",
9205          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9206          callback: yourFunction,
9207          scope: yourObject, //(optional scope)
9208          discardUrl: false,
9209          nocache: false,
9210          text: "Loading...",
9211          timeout: 30,
9212          scripts: false
9213      });
9214      </code></pre>
9215      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9216      * 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.
9217      * @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}
9218      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9219      * @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.
9220      */
9221     load : function(){
9222         var um = this.el.getUpdateManager();
9223         um.update.apply(um, arguments);
9224     },
9225
9226     render : function(el, response){
9227         this.clearSelections();
9228         this.el.update("");
9229         var o;
9230         try{
9231             o = Roo.util.JSON.decode(response.responseText);
9232             if(this.jsonRoot){
9233                 
9234                 o = o[this.jsonRoot];
9235             }
9236         } catch(e){
9237         }
9238         /**
9239          * The current JSON data or null
9240          */
9241         this.jsonData = o;
9242         this.beforeRender();
9243         this.refresh();
9244     },
9245
9246 /**
9247  * Get the number of records in the current JSON dataset
9248  * @return {Number}
9249  */
9250     getCount : function(){
9251         return this.jsonData ? this.jsonData.length : 0;
9252     },
9253
9254 /**
9255  * Returns the JSON object for the specified node(s)
9256  * @param {HTMLElement/Array} node The node or an array of nodes
9257  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9258  * you get the JSON object for the node
9259  */
9260     getNodeData : function(node){
9261         if(node instanceof Array){
9262             var data = [];
9263             for(var i = 0, len = node.length; i < len; i++){
9264                 data.push(this.getNodeData(node[i]));
9265             }
9266             return data;
9267         }
9268         return this.jsonData[this.indexOf(node)] || null;
9269     },
9270
9271     beforeRender : function(){
9272         this.snapshot = this.jsonData;
9273         if(this.sortInfo){
9274             this.sort.apply(this, this.sortInfo);
9275         }
9276         this.fireEvent("beforerender", this, this.jsonData);
9277     },
9278
9279     onLoad : function(el, o){
9280         this.fireEvent("load", this, this.jsonData, o);
9281     },
9282
9283     onLoadException : function(el, o){
9284         this.fireEvent("loadexception", this, o);
9285     },
9286
9287 /**
9288  * Filter the data by a specific property.
9289  * @param {String} property A property on your JSON objects
9290  * @param {String/RegExp} value Either string that the property values
9291  * should start with, or a RegExp to test against the property
9292  */
9293     filter : function(property, value){
9294         if(this.jsonData){
9295             var data = [];
9296             var ss = this.snapshot;
9297             if(typeof value == "string"){
9298                 var vlen = value.length;
9299                 if(vlen == 0){
9300                     this.clearFilter();
9301                     return;
9302                 }
9303                 value = value.toLowerCase();
9304                 for(var i = 0, len = ss.length; i < len; i++){
9305                     var o = ss[i];
9306                     if(o[property].substr(0, vlen).toLowerCase() == value){
9307                         data.push(o);
9308                     }
9309                 }
9310             } else if(value.exec){ // regex?
9311                 for(var i = 0, len = ss.length; i < len; i++){
9312                     var o = ss[i];
9313                     if(value.test(o[property])){
9314                         data.push(o);
9315                     }
9316                 }
9317             } else{
9318                 return;
9319             }
9320             this.jsonData = data;
9321             this.refresh();
9322         }
9323     },
9324
9325 /**
9326  * Filter by a function. The passed function will be called with each
9327  * object in the current dataset. If the function returns true the value is kept,
9328  * otherwise it is filtered.
9329  * @param {Function} fn
9330  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9331  */
9332     filterBy : function(fn, scope){
9333         if(this.jsonData){
9334             var data = [];
9335             var ss = this.snapshot;
9336             for(var i = 0, len = ss.length; i < len; i++){
9337                 var o = ss[i];
9338                 if(fn.call(scope || this, o)){
9339                     data.push(o);
9340                 }
9341             }
9342             this.jsonData = data;
9343             this.refresh();
9344         }
9345     },
9346
9347 /**
9348  * Clears the current filter.
9349  */
9350     clearFilter : function(){
9351         if(this.snapshot && this.jsonData != this.snapshot){
9352             this.jsonData = this.snapshot;
9353             this.refresh();
9354         }
9355     },
9356
9357
9358 /**
9359  * Sorts the data for this view and refreshes it.
9360  * @param {String} property A property on your JSON objects to sort on
9361  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9362  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9363  */
9364     sort : function(property, dir, sortType){
9365         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9366         if(this.jsonData){
9367             var p = property;
9368             var dsc = dir && dir.toLowerCase() == "desc";
9369             var f = function(o1, o2){
9370                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9371                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9372                 ;
9373                 if(v1 < v2){
9374                     return dsc ? +1 : -1;
9375                 } else if(v1 > v2){
9376                     return dsc ? -1 : +1;
9377                 } else{
9378                     return 0;
9379                 }
9380             };
9381             this.jsonData.sort(f);
9382             this.refresh();
9383             if(this.jsonData != this.snapshot){
9384                 this.snapshot.sort(f);
9385             }
9386         }
9387     }
9388 });/*
9389  * Based on:
9390  * Ext JS Library 1.1.1
9391  * Copyright(c) 2006-2007, Ext JS, LLC.
9392  *
9393  * Originally Released Under LGPL - original licence link has changed is not relivant.
9394  *
9395  * Fork - LGPL
9396  * <script type="text/javascript">
9397  */
9398  
9399
9400 /**
9401  * @class Roo.ColorPalette
9402  * @extends Roo.Component
9403  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9404  * Here's an example of typical usage:
9405  * <pre><code>
9406 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9407 cp.render('my-div');
9408
9409 cp.on('select', function(palette, selColor){
9410     // do something with selColor
9411 });
9412 </code></pre>
9413  * @constructor
9414  * Create a new ColorPalette
9415  * @param {Object} config The config object
9416  */
9417 Roo.ColorPalette = function(config){
9418     Roo.ColorPalette.superclass.constructor.call(this, config);
9419     this.addEvents({
9420         /**
9421              * @event select
9422              * Fires when a color is selected
9423              * @param {ColorPalette} this
9424              * @param {String} color The 6-digit color hex code (without the # symbol)
9425              */
9426         select: true
9427     });
9428
9429     if(this.handler){
9430         this.on("select", this.handler, this.scope, true);
9431     }
9432 };
9433 Roo.extend(Roo.ColorPalette, Roo.Component, {
9434     /**
9435      * @cfg {String} itemCls
9436      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9437      */
9438     itemCls : "x-color-palette",
9439     /**
9440      * @cfg {String} value
9441      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9442      * the hex codes are case-sensitive.
9443      */
9444     value : null,
9445     clickEvent:'click',
9446     // private
9447     ctype: "Roo.ColorPalette",
9448
9449     /**
9450      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9451      */
9452     allowReselect : false,
9453
9454     /**
9455      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9456      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9457      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9458      * of colors with the width setting until the box is symmetrical.</p>
9459      * <p>You can override individual colors if needed:</p>
9460      * <pre><code>
9461 var cp = new Roo.ColorPalette();
9462 cp.colors[0] = "FF0000";  // change the first box to red
9463 </code></pre>
9464
9465 Or you can provide a custom array of your own for complete control:
9466 <pre><code>
9467 var cp = new Roo.ColorPalette();
9468 cp.colors = ["000000", "993300", "333300"];
9469 </code></pre>
9470      * @type Array
9471      */
9472     colors : [
9473         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9474         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9475         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9476         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9477         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9478     ],
9479
9480     // private
9481     onRender : function(container, position){
9482         var t = new Roo.MasterTemplate(
9483             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
9484         );
9485         var c = this.colors;
9486         for(var i = 0, len = c.length; i < len; i++){
9487             t.add([c[i]]);
9488         }
9489         var el = document.createElement("div");
9490         el.className = this.itemCls;
9491         t.overwrite(el);
9492         container.dom.insertBefore(el, position);
9493         this.el = Roo.get(el);
9494         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
9495         if(this.clickEvent != 'click'){
9496             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
9497         }
9498     },
9499
9500     // private
9501     afterRender : function(){
9502         Roo.ColorPalette.superclass.afterRender.call(this);
9503         if(this.value){
9504             var s = this.value;
9505             this.value = null;
9506             this.select(s);
9507         }
9508     },
9509
9510     // private
9511     handleClick : function(e, t){
9512         e.preventDefault();
9513         if(!this.disabled){
9514             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
9515             this.select(c.toUpperCase());
9516         }
9517     },
9518
9519     /**
9520      * Selects the specified color in the palette (fires the select event)
9521      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
9522      */
9523     select : function(color){
9524         color = color.replace("#", "");
9525         if(color != this.value || this.allowReselect){
9526             var el = this.el;
9527             if(this.value){
9528                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
9529             }
9530             el.child("a.color-"+color).addClass("x-color-palette-sel");
9531             this.value = color;
9532             this.fireEvent("select", this, color);
9533         }
9534     }
9535 });/*
9536  * Based on:
9537  * Ext JS Library 1.1.1
9538  * Copyright(c) 2006-2007, Ext JS, LLC.
9539  *
9540  * Originally Released Under LGPL - original licence link has changed is not relivant.
9541  *
9542  * Fork - LGPL
9543  * <script type="text/javascript">
9544  */
9545  
9546 /**
9547  * @class Roo.DatePicker
9548  * @extends Roo.Component
9549  * Simple date picker class.
9550  * @constructor
9551  * Create a new DatePicker
9552  * @param {Object} config The config object
9553  */
9554 Roo.DatePicker = function(config){
9555     Roo.DatePicker.superclass.constructor.call(this, config);
9556
9557     this.value = config && config.value ?
9558                  config.value.clearTime() : new Date().clearTime();
9559
9560     this.addEvents({
9561         /**
9562              * @event select
9563              * Fires when a date is selected
9564              * @param {DatePicker} this
9565              * @param {Date} date The selected date
9566              */
9567         'select': true,
9568         /**
9569              * @event monthchange
9570              * Fires when the displayed month changes 
9571              * @param {DatePicker} this
9572              * @param {Date} date The selected month
9573              */
9574         'monthchange': true
9575     });
9576
9577     if(this.handler){
9578         this.on("select", this.handler,  this.scope || this);
9579     }
9580     // build the disabledDatesRE
9581     if(!this.disabledDatesRE && this.disabledDates){
9582         var dd = this.disabledDates;
9583         var re = "(?:";
9584         for(var i = 0; i < dd.length; i++){
9585             re += dd[i];
9586             if(i != dd.length-1) re += "|";
9587         }
9588         this.disabledDatesRE = new RegExp(re + ")");
9589     }
9590 };
9591
9592 Roo.extend(Roo.DatePicker, Roo.Component, {
9593     /**
9594      * @cfg {String} todayText
9595      * The text to display on the button that selects the current date (defaults to "Today")
9596      */
9597     todayText : "Today",
9598     /**
9599      * @cfg {String} okText
9600      * The text to display on the ok button
9601      */
9602     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
9603     /**
9604      * @cfg {String} cancelText
9605      * The text to display on the cancel button
9606      */
9607     cancelText : "Cancel",
9608     /**
9609      * @cfg {String} todayTip
9610      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
9611      */
9612     todayTip : "{0} (Spacebar)",
9613     /**
9614      * @cfg {Date} minDate
9615      * Minimum allowable date (JavaScript date object, defaults to null)
9616      */
9617     minDate : null,
9618     /**
9619      * @cfg {Date} maxDate
9620      * Maximum allowable date (JavaScript date object, defaults to null)
9621      */
9622     maxDate : null,
9623     /**
9624      * @cfg {String} minText
9625      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
9626      */
9627     minText : "This date is before the minimum date",
9628     /**
9629      * @cfg {String} maxText
9630      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
9631      */
9632     maxText : "This date is after the maximum date",
9633     /**
9634      * @cfg {String} format
9635      * The default date format string which can be overriden for localization support.  The format must be
9636      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
9637      */
9638     format : "m/d/y",
9639     /**
9640      * @cfg {Array} disabledDays
9641      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
9642      */
9643     disabledDays : null,
9644     /**
9645      * @cfg {String} disabledDaysText
9646      * The tooltip to display when the date falls on a disabled day (defaults to "")
9647      */
9648     disabledDaysText : "",
9649     /**
9650      * @cfg {RegExp} disabledDatesRE
9651      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
9652      */
9653     disabledDatesRE : null,
9654     /**
9655      * @cfg {String} disabledDatesText
9656      * The tooltip text to display when the date falls on a disabled date (defaults to "")
9657      */
9658     disabledDatesText : "",
9659     /**
9660      * @cfg {Boolean} constrainToViewport
9661      * True to constrain the date picker to the viewport (defaults to true)
9662      */
9663     constrainToViewport : true,
9664     /**
9665      * @cfg {Array} monthNames
9666      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
9667      */
9668     monthNames : Date.monthNames,
9669     /**
9670      * @cfg {Array} dayNames
9671      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
9672      */
9673     dayNames : Date.dayNames,
9674     /**
9675      * @cfg {String} nextText
9676      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
9677      */
9678     nextText: 'Next Month (Control+Right)',
9679     /**
9680      * @cfg {String} prevText
9681      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
9682      */
9683     prevText: 'Previous Month (Control+Left)',
9684     /**
9685      * @cfg {String} monthYearText
9686      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
9687      */
9688     monthYearText: 'Choose a month (Control+Up/Down to move years)',
9689     /**
9690      * @cfg {Number} startDay
9691      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9692      */
9693     startDay : 0,
9694     /**
9695      * @cfg {Bool} showClear
9696      * Show a clear button (usefull for date form elements that can be blank.)
9697      */
9698     
9699     showClear: false,
9700     
9701     /**
9702      * Sets the value of the date field
9703      * @param {Date} value The date to set
9704      */
9705     setValue : function(value){
9706         var old = this.value;
9707         
9708         if (typeof(value) == 'string') {
9709          
9710             value = Date.parseDate(value, this.format);
9711         }
9712         if (!value) {
9713             value = new Date();
9714         }
9715         
9716         this.value = value.clearTime(true);
9717         if(this.el){
9718             this.update(this.value);
9719         }
9720     },
9721
9722     /**
9723      * Gets the current selected value of the date field
9724      * @return {Date} The selected date
9725      */
9726     getValue : function(){
9727         return this.value;
9728     },
9729
9730     // private
9731     focus : function(){
9732         if(this.el){
9733             this.update(this.activeDate);
9734         }
9735     },
9736
9737     // privateval
9738     onRender : function(container, position){
9739         
9740         var m = [
9741              '<table cellspacing="0">',
9742                 '<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>',
9743                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
9744         var dn = this.dayNames;
9745         for(var i = 0; i < 7; i++){
9746             var d = this.startDay+i;
9747             if(d > 6){
9748                 d = d-7;
9749             }
9750             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
9751         }
9752         m[m.length] = "</tr></thead><tbody><tr>";
9753         for(var i = 0; i < 42; i++) {
9754             if(i % 7 == 0 && i != 0){
9755                 m[m.length] = "</tr><tr>";
9756             }
9757             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
9758         }
9759         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
9760             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
9761
9762         var el = document.createElement("div");
9763         el.className = "x-date-picker";
9764         el.innerHTML = m.join("");
9765
9766         container.dom.insertBefore(el, position);
9767
9768         this.el = Roo.get(el);
9769         this.eventEl = Roo.get(el.firstChild);
9770
9771         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
9772             handler: this.showPrevMonth,
9773             scope: this,
9774             preventDefault:true,
9775             stopDefault:true
9776         });
9777
9778         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
9779             handler: this.showNextMonth,
9780             scope: this,
9781             preventDefault:true,
9782             stopDefault:true
9783         });
9784
9785         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
9786
9787         this.monthPicker = this.el.down('div.x-date-mp');
9788         this.monthPicker.enableDisplayMode('block');
9789         
9790         var kn = new Roo.KeyNav(this.eventEl, {
9791             "left" : function(e){
9792                 e.ctrlKey ?
9793                     this.showPrevMonth() :
9794                     this.update(this.activeDate.add("d", -1));
9795             },
9796
9797             "right" : function(e){
9798                 e.ctrlKey ?
9799                     this.showNextMonth() :
9800                     this.update(this.activeDate.add("d", 1));
9801             },
9802
9803             "up" : function(e){
9804                 e.ctrlKey ?
9805                     this.showNextYear() :
9806                     this.update(this.activeDate.add("d", -7));
9807             },
9808
9809             "down" : function(e){
9810                 e.ctrlKey ?
9811                     this.showPrevYear() :
9812                     this.update(this.activeDate.add("d", 7));
9813             },
9814
9815             "pageUp" : function(e){
9816                 this.showNextMonth();
9817             },
9818
9819             "pageDown" : function(e){
9820                 this.showPrevMonth();
9821             },
9822
9823             "enter" : function(e){
9824                 e.stopPropagation();
9825                 return true;
9826             },
9827
9828             scope : this
9829         });
9830
9831         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
9832
9833         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
9834
9835         this.el.unselectable();
9836         
9837         this.cells = this.el.select("table.x-date-inner tbody td");
9838         this.textNodes = this.el.query("table.x-date-inner tbody span");
9839
9840         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
9841             text: "&#160;",
9842             tooltip: this.monthYearText
9843         });
9844
9845         this.mbtn.on('click', this.showMonthPicker, this);
9846         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
9847
9848
9849         var today = (new Date()).dateFormat(this.format);
9850         
9851         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
9852         if (this.showClear) {
9853             baseTb.add( new Roo.Toolbar.Fill());
9854         }
9855         baseTb.add({
9856             text: String.format(this.todayText, today),
9857             tooltip: String.format(this.todayTip, today),
9858             handler: this.selectToday,
9859             scope: this
9860         });
9861         
9862         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
9863             
9864         //});
9865         if (this.showClear) {
9866             
9867             baseTb.add( new Roo.Toolbar.Fill());
9868             baseTb.add({
9869                 text: '&#160;',
9870                 cls: 'x-btn-icon x-btn-clear',
9871                 handler: function() {
9872                     //this.value = '';
9873                     this.fireEvent("select", this, '');
9874                 },
9875                 scope: this
9876             });
9877         }
9878         
9879         
9880         if(Roo.isIE){
9881             this.el.repaint();
9882         }
9883         this.update(this.value);
9884     },
9885
9886     createMonthPicker : function(){
9887         if(!this.monthPicker.dom.firstChild){
9888             var buf = ['<table border="0" cellspacing="0">'];
9889             for(var i = 0; i < 6; i++){
9890                 buf.push(
9891                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
9892                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
9893                     i == 0 ?
9894                     '<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>' :
9895                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
9896                 );
9897             }
9898             buf.push(
9899                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
9900                     this.okText,
9901                     '</button><button type="button" class="x-date-mp-cancel">',
9902                     this.cancelText,
9903                     '</button></td></tr>',
9904                 '</table>'
9905             );
9906             this.monthPicker.update(buf.join(''));
9907             this.monthPicker.on('click', this.onMonthClick, this);
9908             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
9909
9910             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
9911             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
9912
9913             this.mpMonths.each(function(m, a, i){
9914                 i += 1;
9915                 if((i%2) == 0){
9916                     m.dom.xmonth = 5 + Math.round(i * .5);
9917                 }else{
9918                     m.dom.xmonth = Math.round((i-1) * .5);
9919                 }
9920             });
9921         }
9922     },
9923
9924     showMonthPicker : function(){
9925         this.createMonthPicker();
9926         var size = this.el.getSize();
9927         this.monthPicker.setSize(size);
9928         this.monthPicker.child('table').setSize(size);
9929
9930         this.mpSelMonth = (this.activeDate || this.value).getMonth();
9931         this.updateMPMonth(this.mpSelMonth);
9932         this.mpSelYear = (this.activeDate || this.value).getFullYear();
9933         this.updateMPYear(this.mpSelYear);
9934
9935         this.monthPicker.slideIn('t', {duration:.2});
9936     },
9937
9938     updateMPYear : function(y){
9939         this.mpyear = y;
9940         var ys = this.mpYears.elements;
9941         for(var i = 1; i <= 10; i++){
9942             var td = ys[i-1], y2;
9943             if((i%2) == 0){
9944                 y2 = y + Math.round(i * .5);
9945                 td.firstChild.innerHTML = y2;
9946                 td.xyear = y2;
9947             }else{
9948                 y2 = y - (5-Math.round(i * .5));
9949                 td.firstChild.innerHTML = y2;
9950                 td.xyear = y2;
9951             }
9952             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
9953         }
9954     },
9955
9956     updateMPMonth : function(sm){
9957         this.mpMonths.each(function(m, a, i){
9958             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
9959         });
9960     },
9961
9962     selectMPMonth: function(m){
9963         
9964     },
9965
9966     onMonthClick : function(e, t){
9967         e.stopEvent();
9968         var el = new Roo.Element(t), pn;
9969         if(el.is('button.x-date-mp-cancel')){
9970             this.hideMonthPicker();
9971         }
9972         else if(el.is('button.x-date-mp-ok')){
9973             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
9974             this.hideMonthPicker();
9975         }
9976         else if(pn = el.up('td.x-date-mp-month', 2)){
9977             this.mpMonths.removeClass('x-date-mp-sel');
9978             pn.addClass('x-date-mp-sel');
9979             this.mpSelMonth = pn.dom.xmonth;
9980         }
9981         else if(pn = el.up('td.x-date-mp-year', 2)){
9982             this.mpYears.removeClass('x-date-mp-sel');
9983             pn.addClass('x-date-mp-sel');
9984             this.mpSelYear = pn.dom.xyear;
9985         }
9986         else if(el.is('a.x-date-mp-prev')){
9987             this.updateMPYear(this.mpyear-10);
9988         }
9989         else if(el.is('a.x-date-mp-next')){
9990             this.updateMPYear(this.mpyear+10);
9991         }
9992     },
9993
9994     onMonthDblClick : function(e, t){
9995         e.stopEvent();
9996         var el = new Roo.Element(t), pn;
9997         if(pn = el.up('td.x-date-mp-month', 2)){
9998             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
9999             this.hideMonthPicker();
10000         }
10001         else if(pn = el.up('td.x-date-mp-year', 2)){
10002             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10003             this.hideMonthPicker();
10004         }
10005     },
10006
10007     hideMonthPicker : function(disableAnim){
10008         if(this.monthPicker){
10009             if(disableAnim === true){
10010                 this.monthPicker.hide();
10011             }else{
10012                 this.monthPicker.slideOut('t', {duration:.2});
10013             }
10014         }
10015     },
10016
10017     // private
10018     showPrevMonth : function(e){
10019         this.update(this.activeDate.add("mo", -1));
10020     },
10021
10022     // private
10023     showNextMonth : function(e){
10024         this.update(this.activeDate.add("mo", 1));
10025     },
10026
10027     // private
10028     showPrevYear : function(){
10029         this.update(this.activeDate.add("y", -1));
10030     },
10031
10032     // private
10033     showNextYear : function(){
10034         this.update(this.activeDate.add("y", 1));
10035     },
10036
10037     // private
10038     handleMouseWheel : function(e){
10039         var delta = e.getWheelDelta();
10040         if(delta > 0){
10041             this.showPrevMonth();
10042             e.stopEvent();
10043         } else if(delta < 0){
10044             this.showNextMonth();
10045             e.stopEvent();
10046         }
10047     },
10048
10049     // private
10050     handleDateClick : function(e, t){
10051         e.stopEvent();
10052         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10053             this.setValue(new Date(t.dateValue));
10054             this.fireEvent("select", this, this.value);
10055         }
10056     },
10057
10058     // private
10059     selectToday : function(){
10060         this.setValue(new Date().clearTime());
10061         this.fireEvent("select", this, this.value);
10062     },
10063
10064     // private
10065     update : function(date)
10066     {
10067         var vd = this.activeDate;
10068         this.activeDate = date;
10069         if(vd && this.el){
10070             var t = date.getTime();
10071             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10072                 this.cells.removeClass("x-date-selected");
10073                 this.cells.each(function(c){
10074                    if(c.dom.firstChild.dateValue == t){
10075                        c.addClass("x-date-selected");
10076                        setTimeout(function(){
10077                             try{c.dom.firstChild.focus();}catch(e){}
10078                        }, 50);
10079                        return false;
10080                    }
10081                 });
10082                 return;
10083             }
10084         }
10085         
10086         var days = date.getDaysInMonth();
10087         var firstOfMonth = date.getFirstDateOfMonth();
10088         var startingPos = firstOfMonth.getDay()-this.startDay;
10089
10090         if(startingPos <= this.startDay){
10091             startingPos += 7;
10092         }
10093
10094         var pm = date.add("mo", -1);
10095         var prevStart = pm.getDaysInMonth()-startingPos;
10096
10097         var cells = this.cells.elements;
10098         var textEls = this.textNodes;
10099         days += startingPos;
10100
10101         // convert everything to numbers so it's fast
10102         var day = 86400000;
10103         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10104         var today = new Date().clearTime().getTime();
10105         var sel = date.clearTime().getTime();
10106         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10107         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10108         var ddMatch = this.disabledDatesRE;
10109         var ddText = this.disabledDatesText;
10110         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10111         var ddaysText = this.disabledDaysText;
10112         var format = this.format;
10113
10114         var setCellClass = function(cal, cell){
10115             cell.title = "";
10116             var t = d.getTime();
10117             cell.firstChild.dateValue = t;
10118             if(t == today){
10119                 cell.className += " x-date-today";
10120                 cell.title = cal.todayText;
10121             }
10122             if(t == sel){
10123                 cell.className += " x-date-selected";
10124                 setTimeout(function(){
10125                     try{cell.firstChild.focus();}catch(e){}
10126                 }, 50);
10127             }
10128             // disabling
10129             if(t < min) {
10130                 cell.className = " x-date-disabled";
10131                 cell.title = cal.minText;
10132                 return;
10133             }
10134             if(t > max) {
10135                 cell.className = " x-date-disabled";
10136                 cell.title = cal.maxText;
10137                 return;
10138             }
10139             if(ddays){
10140                 if(ddays.indexOf(d.getDay()) != -1){
10141                     cell.title = ddaysText;
10142                     cell.className = " x-date-disabled";
10143                 }
10144             }
10145             if(ddMatch && format){
10146                 var fvalue = d.dateFormat(format);
10147                 if(ddMatch.test(fvalue)){
10148                     cell.title = ddText.replace("%0", fvalue);
10149                     cell.className = " x-date-disabled";
10150                 }
10151             }
10152         };
10153
10154         var i = 0;
10155         for(; i < startingPos; i++) {
10156             textEls[i].innerHTML = (++prevStart);
10157             d.setDate(d.getDate()+1);
10158             cells[i].className = "x-date-prevday";
10159             setCellClass(this, cells[i]);
10160         }
10161         for(; i < days; i++){
10162             intDay = i - startingPos + 1;
10163             textEls[i].innerHTML = (intDay);
10164             d.setDate(d.getDate()+1);
10165             cells[i].className = "x-date-active";
10166             setCellClass(this, cells[i]);
10167         }
10168         var extraDays = 0;
10169         for(; i < 42; i++) {
10170              textEls[i].innerHTML = (++extraDays);
10171              d.setDate(d.getDate()+1);
10172              cells[i].className = "x-date-nextday";
10173              setCellClass(this, cells[i]);
10174         }
10175
10176         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10177         this.fireEvent('monthchange', this, date);
10178         
10179         if(!this.internalRender){
10180             var main = this.el.dom.firstChild;
10181             var w = main.offsetWidth;
10182             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10183             Roo.fly(main).setWidth(w);
10184             this.internalRender = true;
10185             // opera does not respect the auto grow header center column
10186             // then, after it gets a width opera refuses to recalculate
10187             // without a second pass
10188             if(Roo.isOpera && !this.secondPass){
10189                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10190                 this.secondPass = true;
10191                 this.update.defer(10, this, [date]);
10192             }
10193         }
10194         
10195         
10196     }
10197 });        /*
10198  * Based on:
10199  * Ext JS Library 1.1.1
10200  * Copyright(c) 2006-2007, Ext JS, LLC.
10201  *
10202  * Originally Released Under LGPL - original licence link has changed is not relivant.
10203  *
10204  * Fork - LGPL
10205  * <script type="text/javascript">
10206  */
10207 /**
10208  * @class Roo.TabPanel
10209  * @extends Roo.util.Observable
10210  * A lightweight tab container.
10211  * <br><br>
10212  * Usage:
10213  * <pre><code>
10214 // basic tabs 1, built from existing content
10215 var tabs = new Roo.TabPanel("tabs1");
10216 tabs.addTab("script", "View Script");
10217 tabs.addTab("markup", "View Markup");
10218 tabs.activate("script");
10219
10220 // more advanced tabs, built from javascript
10221 var jtabs = new Roo.TabPanel("jtabs");
10222 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10223
10224 // set up the UpdateManager
10225 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10226 var updater = tab2.getUpdateManager();
10227 updater.setDefaultUrl("ajax1.htm");
10228 tab2.on('activate', updater.refresh, updater, true);
10229
10230 // Use setUrl for Ajax loading
10231 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10232 tab3.setUrl("ajax2.htm", null, true);
10233
10234 // Disabled tab
10235 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10236 tab4.disable();
10237
10238 jtabs.activate("jtabs-1");
10239  * </code></pre>
10240  * @constructor
10241  * Create a new TabPanel.
10242  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10243  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10244  */
10245 Roo.TabPanel = function(container, config){
10246     /**
10247     * The container element for this TabPanel.
10248     * @type Roo.Element
10249     */
10250     this.el = Roo.get(container, true);
10251     if(config){
10252         if(typeof config == "boolean"){
10253             this.tabPosition = config ? "bottom" : "top";
10254         }else{
10255             Roo.apply(this, config);
10256         }
10257     }
10258     if(this.tabPosition == "bottom"){
10259         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10260         this.el.addClass("x-tabs-bottom");
10261     }
10262     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10263     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10264     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10265     if(Roo.isIE){
10266         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10267     }
10268     if(this.tabPosition != "bottom"){
10269         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10270          * @type Roo.Element
10271          */
10272         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10273         this.el.addClass("x-tabs-top");
10274     }
10275     this.items = [];
10276
10277     this.bodyEl.setStyle("position", "relative");
10278
10279     this.active = null;
10280     this.activateDelegate = this.activate.createDelegate(this);
10281
10282     this.addEvents({
10283         /**
10284          * @event tabchange
10285          * Fires when the active tab changes
10286          * @param {Roo.TabPanel} this
10287          * @param {Roo.TabPanelItem} activePanel The new active tab
10288          */
10289         "tabchange": true,
10290         /**
10291          * @event beforetabchange
10292          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10293          * @param {Roo.TabPanel} this
10294          * @param {Object} e Set cancel to true on this object to cancel the tab change
10295          * @param {Roo.TabPanelItem} tab The tab being changed to
10296          */
10297         "beforetabchange" : true
10298     });
10299
10300     Roo.EventManager.onWindowResize(this.onResize, this);
10301     this.cpad = this.el.getPadding("lr");
10302     this.hiddenCount = 0;
10303
10304
10305     // toolbar on the tabbar support...
10306     if (this.toolbar) {
10307         var tcfg = this.toolbar;
10308         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
10309         this.toolbar = new Roo.Toolbar(tcfg);
10310         if (Roo.isSafari) {
10311             var tbl = tcfg.container.child('table', true);
10312             tbl.setAttribute('width', '100%');
10313         }
10314         
10315     }
10316    
10317
10318
10319     Roo.TabPanel.superclass.constructor.call(this);
10320 };
10321
10322 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10323     /*
10324      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10325      */
10326     tabPosition : "top",
10327     /*
10328      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10329      */
10330     currentTabWidth : 0,
10331     /*
10332      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10333      */
10334     minTabWidth : 40,
10335     /*
10336      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10337      */
10338     maxTabWidth : 250,
10339     /*
10340      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10341      */
10342     preferredTabWidth : 175,
10343     /*
10344      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10345      */
10346     resizeTabs : false,
10347     /*
10348      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10349      */
10350     monitorResize : true,
10351     /*
10352      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
10353      */
10354     toolbar : false,
10355
10356     /**
10357      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10358      * @param {String} id The id of the div to use <b>or create</b>
10359      * @param {String} text The text for the tab
10360      * @param {String} content (optional) Content to put in the TabPanelItem body
10361      * @param {Boolean} closable (optional) True to create a close icon on the tab
10362      * @return {Roo.TabPanelItem} The created TabPanelItem
10363      */
10364     addTab : function(id, text, content, closable){
10365         var item = new Roo.TabPanelItem(this, id, text, closable);
10366         this.addTabItem(item);
10367         if(content){
10368             item.setContent(content);
10369         }
10370         return item;
10371     },
10372
10373     /**
10374      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10375      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10376      * @return {Roo.TabPanelItem}
10377      */
10378     getTab : function(id){
10379         return this.items[id];
10380     },
10381
10382     /**
10383      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10384      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10385      */
10386     hideTab : function(id){
10387         var t = this.items[id];
10388         if(!t.isHidden()){
10389            t.setHidden(true);
10390            this.hiddenCount++;
10391            this.autoSizeTabs();
10392         }
10393     },
10394
10395     /**
10396      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10397      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10398      */
10399     unhideTab : function(id){
10400         var t = this.items[id];
10401         if(t.isHidden()){
10402            t.setHidden(false);
10403            this.hiddenCount--;
10404            this.autoSizeTabs();
10405         }
10406     },
10407
10408     /**
10409      * Adds an existing {@link Roo.TabPanelItem}.
10410      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10411      */
10412     addTabItem : function(item){
10413         this.items[item.id] = item;
10414         this.items.push(item);
10415         if(this.resizeTabs){
10416            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10417            this.autoSizeTabs();
10418         }else{
10419             item.autoSize();
10420         }
10421     },
10422
10423     /**
10424      * Removes a {@link Roo.TabPanelItem}.
10425      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10426      */
10427     removeTab : function(id){
10428         var items = this.items;
10429         var tab = items[id];
10430         if(!tab) { return; }
10431         var index = items.indexOf(tab);
10432         if(this.active == tab && items.length > 1){
10433             var newTab = this.getNextAvailable(index);
10434             if(newTab) {
10435                 newTab.activate();
10436             }
10437         }
10438         this.stripEl.dom.removeChild(tab.pnode.dom);
10439         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10440             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10441         }
10442         items.splice(index, 1);
10443         delete this.items[tab.id];
10444         tab.fireEvent("close", tab);
10445         tab.purgeListeners();
10446         this.autoSizeTabs();
10447     },
10448
10449     getNextAvailable : function(start){
10450         var items = this.items;
10451         var index = start;
10452         // look for a next tab that will slide over to
10453         // replace the one being removed
10454         while(index < items.length){
10455             var item = items[++index];
10456             if(item && !item.isHidden()){
10457                 return item;
10458             }
10459         }
10460         // if one isn't found select the previous tab (on the left)
10461         index = start;
10462         while(index >= 0){
10463             var item = items[--index];
10464             if(item && !item.isHidden()){
10465                 return item;
10466             }
10467         }
10468         return null;
10469     },
10470
10471     /**
10472      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10473      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10474      */
10475     disableTab : function(id){
10476         var tab = this.items[id];
10477         if(tab && this.active != tab){
10478             tab.disable();
10479         }
10480     },
10481
10482     /**
10483      * Enables a {@link Roo.TabPanelItem} that is disabled.
10484      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10485      */
10486     enableTab : function(id){
10487         var tab = this.items[id];
10488         tab.enable();
10489     },
10490
10491     /**
10492      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10493      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10494      * @return {Roo.TabPanelItem} The TabPanelItem.
10495      */
10496     activate : function(id){
10497         var tab = this.items[id];
10498         if(!tab){
10499             return null;
10500         }
10501         if(tab == this.active || tab.disabled){
10502             return tab;
10503         }
10504         var e = {};
10505         this.fireEvent("beforetabchange", this, e, tab);
10506         if(e.cancel !== true && !tab.disabled){
10507             if(this.active){
10508                 this.active.hide();
10509             }
10510             this.active = this.items[id];
10511             this.active.show();
10512             this.fireEvent("tabchange", this, this.active);
10513         }
10514         return tab;
10515     },
10516
10517     /**
10518      * Gets the active {@link Roo.TabPanelItem}.
10519      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
10520      */
10521     getActiveTab : function(){
10522         return this.active;
10523     },
10524
10525     /**
10526      * Updates the tab body element to fit the height of the container element
10527      * for overflow scrolling
10528      * @param {Number} targetHeight (optional) Override the starting height from the elements height
10529      */
10530     syncHeight : function(targetHeight){
10531         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
10532         var bm = this.bodyEl.getMargins();
10533         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
10534         this.bodyEl.setHeight(newHeight);
10535         return newHeight;
10536     },
10537
10538     onResize : function(){
10539         if(this.monitorResize){
10540             this.autoSizeTabs();
10541         }
10542     },
10543
10544     /**
10545      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
10546      */
10547     beginUpdate : function(){
10548         this.updating = true;
10549     },
10550
10551     /**
10552      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
10553      */
10554     endUpdate : function(){
10555         this.updating = false;
10556         this.autoSizeTabs();
10557     },
10558
10559     /**
10560      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
10561      */
10562     autoSizeTabs : function(){
10563         var count = this.items.length;
10564         var vcount = count - this.hiddenCount;
10565         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
10566         var w = Math.max(this.el.getWidth() - this.cpad, 10);
10567         var availWidth = Math.floor(w / vcount);
10568         var b = this.stripBody;
10569         if(b.getWidth() > w){
10570             var tabs = this.items;
10571             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
10572             if(availWidth < this.minTabWidth){
10573                 /*if(!this.sleft){    // incomplete scrolling code
10574                     this.createScrollButtons();
10575                 }
10576                 this.showScroll();
10577                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
10578             }
10579         }else{
10580             if(this.currentTabWidth < this.preferredTabWidth){
10581                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
10582             }
10583         }
10584     },
10585
10586     /**
10587      * Returns the number of tabs in this TabPanel.
10588      * @return {Number}
10589      */
10590      getCount : function(){
10591          return this.items.length;
10592      },
10593
10594     /**
10595      * Resizes all the tabs to the passed width
10596      * @param {Number} The new width
10597      */
10598     setTabWidth : function(width){
10599         this.currentTabWidth = width;
10600         for(var i = 0, len = this.items.length; i < len; i++) {
10601                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
10602         }
10603     },
10604
10605     /**
10606      * Destroys this TabPanel
10607      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
10608      */
10609     destroy : function(removeEl){
10610         Roo.EventManager.removeResizeListener(this.onResize, this);
10611         for(var i = 0, len = this.items.length; i < len; i++){
10612             this.items[i].purgeListeners();
10613         }
10614         if(removeEl === true){
10615             this.el.update("");
10616             this.el.remove();
10617         }
10618     }
10619 });
10620
10621 /**
10622  * @class Roo.TabPanelItem
10623  * @extends Roo.util.Observable
10624  * Represents an individual item (tab plus body) in a TabPanel.
10625  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
10626  * @param {String} id The id of this TabPanelItem
10627  * @param {String} text The text for the tab of this TabPanelItem
10628  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
10629  */
10630 Roo.TabPanelItem = function(tabPanel, id, text, closable){
10631     /**
10632      * The {@link Roo.TabPanel} this TabPanelItem belongs to
10633      * @type Roo.TabPanel
10634      */
10635     this.tabPanel = tabPanel;
10636     /**
10637      * The id for this TabPanelItem
10638      * @type String
10639      */
10640     this.id = id;
10641     /** @private */
10642     this.disabled = false;
10643     /** @private */
10644     this.text = text;
10645     /** @private */
10646     this.loaded = false;
10647     this.closable = closable;
10648
10649     /**
10650      * The body element for this TabPanelItem.
10651      * @type Roo.Element
10652      */
10653     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
10654     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
10655     this.bodyEl.setStyle("display", "block");
10656     this.bodyEl.setStyle("zoom", "1");
10657     this.hideAction();
10658
10659     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
10660     /** @private */
10661     this.el = Roo.get(els.el, true);
10662     this.inner = Roo.get(els.inner, true);
10663     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
10664     this.pnode = Roo.get(els.el.parentNode, true);
10665     this.el.on("mousedown", this.onTabMouseDown, this);
10666     this.el.on("click", this.onTabClick, this);
10667     /** @private */
10668     if(closable){
10669         var c = Roo.get(els.close, true);
10670         c.dom.title = this.closeText;
10671         c.addClassOnOver("close-over");
10672         c.on("click", this.closeClick, this);
10673      }
10674
10675     this.addEvents({
10676          /**
10677          * @event activate
10678          * Fires when this tab becomes the active tab.
10679          * @param {Roo.TabPanel} tabPanel The parent TabPanel
10680          * @param {Roo.TabPanelItem} this
10681          */
10682         "activate": true,
10683         /**
10684          * @event beforeclose
10685          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
10686          * @param {Roo.TabPanelItem} this
10687          * @param {Object} e Set cancel to true on this object to cancel the close.
10688          */
10689         "beforeclose": true,
10690         /**
10691          * @event close
10692          * Fires when this tab is closed.
10693          * @param {Roo.TabPanelItem} this
10694          */
10695          "close": true,
10696         /**
10697          * @event deactivate
10698          * Fires when this tab is no longer the active tab.
10699          * @param {Roo.TabPanel} tabPanel The parent TabPanel
10700          * @param {Roo.TabPanelItem} this
10701          */
10702          "deactivate" : true
10703     });
10704     this.hidden = false;
10705
10706     Roo.TabPanelItem.superclass.constructor.call(this);
10707 };
10708
10709 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
10710     purgeListeners : function(){
10711        Roo.util.Observable.prototype.purgeListeners.call(this);
10712        this.el.removeAllListeners();
10713     },
10714     /**
10715      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
10716      */
10717     show : function(){
10718         this.pnode.addClass("on");
10719         this.showAction();
10720         if(Roo.isOpera){
10721             this.tabPanel.stripWrap.repaint();
10722         }
10723         this.fireEvent("activate", this.tabPanel, this);
10724     },
10725
10726     /**
10727      * Returns true if this tab is the active tab.
10728      * @return {Boolean}
10729      */
10730     isActive : function(){
10731         return this.tabPanel.getActiveTab() == this;
10732     },
10733
10734     /**
10735      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
10736      */
10737     hide : function(){
10738         this.pnode.removeClass("on");
10739         this.hideAction();
10740         this.fireEvent("deactivate", this.tabPanel, this);
10741     },
10742
10743     hideAction : function(){
10744         this.bodyEl.hide();
10745         this.bodyEl.setStyle("position", "absolute");
10746         this.bodyEl.setLeft("-20000px");
10747         this.bodyEl.setTop("-20000px");
10748     },
10749
10750     showAction : function(){
10751         this.bodyEl.setStyle("position", "relative");
10752         this.bodyEl.setTop("");
10753         this.bodyEl.setLeft("");
10754         this.bodyEl.show();
10755     },
10756
10757     /**
10758      * Set the tooltip for the tab.
10759      * @param {String} tooltip The tab's tooltip
10760      */
10761     setTooltip : function(text){
10762         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
10763             this.textEl.dom.qtip = text;
10764             this.textEl.dom.removeAttribute('title');
10765         }else{
10766             this.textEl.dom.title = text;
10767         }
10768     },
10769
10770     onTabClick : function(e){
10771         e.preventDefault();
10772         this.tabPanel.activate(this.id);
10773     },
10774
10775     onTabMouseDown : function(e){
10776         e.preventDefault();
10777         this.tabPanel.activate(this.id);
10778     },
10779
10780     getWidth : function(){
10781         return this.inner.getWidth();
10782     },
10783
10784     setWidth : function(width){
10785         var iwidth = width - this.pnode.getPadding("lr");
10786         this.inner.setWidth(iwidth);
10787         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
10788         this.pnode.setWidth(width);
10789     },
10790
10791     /**
10792      * Show or hide the tab
10793      * @param {Boolean} hidden True to hide or false to show.
10794      */
10795     setHidden : function(hidden){
10796         this.hidden = hidden;
10797         this.pnode.setStyle("display", hidden ? "none" : "");
10798     },
10799
10800     /**
10801      * Returns true if this tab is "hidden"
10802      * @return {Boolean}
10803      */
10804     isHidden : function(){
10805         return this.hidden;
10806     },
10807
10808     /**
10809      * Returns the text for this tab
10810      * @return {String}
10811      */
10812     getText : function(){
10813         return this.text;
10814     },
10815
10816     autoSize : function(){
10817         //this.el.beginMeasure();
10818         this.textEl.setWidth(1);
10819         /*
10820          *  #2804 [new] Tabs in Roojs
10821          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
10822          */
10823         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
10824         //this.el.endMeasure();
10825     },
10826
10827     /**
10828      * Sets the text for the tab (Note: this also sets the tooltip text)
10829      * @param {String} text The tab's text and tooltip
10830      */
10831     setText : function(text){
10832         this.text = text;
10833         this.textEl.update(text);
10834         this.setTooltip(text);
10835         if(!this.tabPanel.resizeTabs){
10836             this.autoSize();
10837         }
10838     },
10839     /**
10840      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
10841      */
10842     activate : function(){
10843         this.tabPanel.activate(this.id);
10844     },
10845
10846     /**
10847      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
10848      */
10849     disable : function(){
10850         if(this.tabPanel.active != this){
10851             this.disabled = true;
10852             this.pnode.addClass("disabled");
10853         }
10854     },
10855
10856     /**
10857      * Enables this TabPanelItem if it was previously disabled.
10858      */
10859     enable : function(){
10860         this.disabled = false;
10861         this.pnode.removeClass("disabled");
10862     },
10863
10864     /**
10865      * Sets the content for this TabPanelItem.
10866      * @param {String} content The content
10867      * @param {Boolean} loadScripts true to look for and load scripts
10868      */
10869     setContent : function(content, loadScripts){
10870         this.bodyEl.update(content, loadScripts);
10871     },
10872
10873     /**
10874      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
10875      * @return {Roo.UpdateManager} The UpdateManager
10876      */
10877     getUpdateManager : function(){
10878         return this.bodyEl.getUpdateManager();
10879     },
10880
10881     /**
10882      * Set a URL to be used to load the content for this TabPanelItem.
10883      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
10884      * @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)
10885      * @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)
10886      * @return {Roo.UpdateManager} The UpdateManager
10887      */
10888     setUrl : function(url, params, loadOnce){
10889         if(this.refreshDelegate){
10890             this.un('activate', this.refreshDelegate);
10891         }
10892         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
10893         this.on("activate", this.refreshDelegate);
10894         return this.bodyEl.getUpdateManager();
10895     },
10896
10897     /** @private */
10898     _handleRefresh : function(url, params, loadOnce){
10899         if(!loadOnce || !this.loaded){
10900             var updater = this.bodyEl.getUpdateManager();
10901             updater.update(url, params, this._setLoaded.createDelegate(this));
10902         }
10903     },
10904
10905     /**
10906      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
10907      *   Will fail silently if the setUrl method has not been called.
10908      *   This does not activate the panel, just updates its content.
10909      */
10910     refresh : function(){
10911         if(this.refreshDelegate){
10912            this.loaded = false;
10913            this.refreshDelegate();
10914         }
10915     },
10916
10917     /** @private */
10918     _setLoaded : function(){
10919         this.loaded = true;
10920     },
10921
10922     /** @private */
10923     closeClick : function(e){
10924         var o = {};
10925         e.stopEvent();
10926         this.fireEvent("beforeclose", this, o);
10927         if(o.cancel !== true){
10928             this.tabPanel.removeTab(this.id);
10929         }
10930     },
10931     /**
10932      * The text displayed in the tooltip for the close icon.
10933      * @type String
10934      */
10935     closeText : "Close this tab"
10936 });
10937
10938 /** @private */
10939 Roo.TabPanel.prototype.createStrip = function(container){
10940     var strip = document.createElement("div");
10941     strip.className = "x-tabs-wrap";
10942     container.appendChild(strip);
10943     return strip;
10944 };
10945 /** @private */
10946 Roo.TabPanel.prototype.createStripList = function(strip){
10947     // div wrapper for retard IE
10948     // returns the "tr" element.
10949     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
10950         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
10951         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
10952     return strip.firstChild.firstChild.firstChild.firstChild;
10953 };
10954 /** @private */
10955 Roo.TabPanel.prototype.createBody = function(container){
10956     var body = document.createElement("div");
10957     Roo.id(body, "tab-body");
10958     Roo.fly(body).addClass("x-tabs-body");
10959     container.appendChild(body);
10960     return body;
10961 };
10962 /** @private */
10963 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
10964     var body = Roo.getDom(id);
10965     if(!body){
10966         body = document.createElement("div");
10967         body.id = id;
10968     }
10969     Roo.fly(body).addClass("x-tabs-item-body");
10970     bodyEl.insertBefore(body, bodyEl.firstChild);
10971     return body;
10972 };
10973 /** @private */
10974 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
10975     var td = document.createElement("td");
10976     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
10977     //stripEl.appendChild(td);
10978     if(closable){
10979         td.className = "x-tabs-closable";
10980         if(!this.closeTpl){
10981             this.closeTpl = new Roo.Template(
10982                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
10983                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
10984                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
10985             );
10986         }
10987         var el = this.closeTpl.overwrite(td, {"text": text});
10988         var close = el.getElementsByTagName("div")[0];
10989         var inner = el.getElementsByTagName("em")[0];
10990         return {"el": el, "close": close, "inner": inner};
10991     } else {
10992         if(!this.tabTpl){
10993             this.tabTpl = new Roo.Template(
10994                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
10995                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
10996             );
10997         }
10998         var el = this.tabTpl.overwrite(td, {"text": text});
10999         var inner = el.getElementsByTagName("em")[0];
11000         return {"el": el, "inner": inner};
11001     }
11002 };/*
11003  * Based on:
11004  * Ext JS Library 1.1.1
11005  * Copyright(c) 2006-2007, Ext JS, LLC.
11006  *
11007  * Originally Released Under LGPL - original licence link has changed is not relivant.
11008  *
11009  * Fork - LGPL
11010  * <script type="text/javascript">
11011  */
11012
11013 /**
11014  * @class Roo.Button
11015  * @extends Roo.util.Observable
11016  * Simple Button class
11017  * @cfg {String} text The button text
11018  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11019  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11020  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11021  * @cfg {Object} scope The scope of the handler
11022  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11023  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11024  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11025  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11026  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11027  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11028    applies if enableToggle = true)
11029  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11030  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11031   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11032  * @constructor
11033  * Create a new button
11034  * @param {Object} config The config object
11035  */
11036 Roo.Button = function(renderTo, config)
11037 {
11038     if (!config) {
11039         config = renderTo;
11040         renderTo = config.renderTo || false;
11041     }
11042     
11043     Roo.apply(this, config);
11044     this.addEvents({
11045         /**
11046              * @event click
11047              * Fires when this button is clicked
11048              * @param {Button} this
11049              * @param {EventObject} e The click event
11050              */
11051             "click" : true,
11052         /**
11053              * @event toggle
11054              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11055              * @param {Button} this
11056              * @param {Boolean} pressed
11057              */
11058             "toggle" : true,
11059         /**
11060              * @event mouseover
11061              * Fires when the mouse hovers over the button
11062              * @param {Button} this
11063              * @param {Event} e The event object
11064              */
11065         'mouseover' : true,
11066         /**
11067              * @event mouseout
11068              * Fires when the mouse exits the button
11069              * @param {Button} this
11070              * @param {Event} e The event object
11071              */
11072         'mouseout': true,
11073          /**
11074              * @event render
11075              * Fires when the button is rendered
11076              * @param {Button} this
11077              */
11078         'render': true
11079     });
11080     if(this.menu){
11081         this.menu = Roo.menu.MenuMgr.get(this.menu);
11082     }
11083     // register listeners first!!  - so render can be captured..
11084     Roo.util.Observable.call(this);
11085     if(renderTo){
11086         this.render(renderTo);
11087     }
11088     
11089   
11090 };
11091
11092 Roo.extend(Roo.Button, Roo.util.Observable, {
11093     /**
11094      * 
11095      */
11096     
11097     /**
11098      * Read-only. True if this button is hidden
11099      * @type Boolean
11100      */
11101     hidden : false,
11102     /**
11103      * Read-only. True if this button is disabled
11104      * @type Boolean
11105      */
11106     disabled : false,
11107     /**
11108      * Read-only. True if this button is pressed (only if enableToggle = true)
11109      * @type Boolean
11110      */
11111     pressed : false,
11112
11113     /**
11114      * @cfg {Number} tabIndex 
11115      * The DOM tabIndex for this button (defaults to undefined)
11116      */
11117     tabIndex : undefined,
11118
11119     /**
11120      * @cfg {Boolean} enableToggle
11121      * True to enable pressed/not pressed toggling (defaults to false)
11122      */
11123     enableToggle: false,
11124     /**
11125      * @cfg {Mixed} menu
11126      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11127      */
11128     menu : undefined,
11129     /**
11130      * @cfg {String} menuAlign
11131      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11132      */
11133     menuAlign : "tl-bl?",
11134
11135     /**
11136      * @cfg {String} iconCls
11137      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11138      */
11139     iconCls : undefined,
11140     /**
11141      * @cfg {String} type
11142      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11143      */
11144     type : 'button',
11145
11146     // private
11147     menuClassTarget: 'tr',
11148
11149     /**
11150      * @cfg {String} clickEvent
11151      * The type of event to map to the button's event handler (defaults to 'click')
11152      */
11153     clickEvent : 'click',
11154
11155     /**
11156      * @cfg {Boolean} handleMouseEvents
11157      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11158      */
11159     handleMouseEvents : true,
11160
11161     /**
11162      * @cfg {String} tooltipType
11163      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11164      */
11165     tooltipType : 'qtip',
11166
11167     /**
11168      * @cfg {String} cls
11169      * A CSS class to apply to the button's main element.
11170      */
11171     
11172     /**
11173      * @cfg {Roo.Template} template (Optional)
11174      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11175      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11176      * require code modifications if required elements (e.g. a button) aren't present.
11177      */
11178
11179     // private
11180     render : function(renderTo){
11181         var btn;
11182         if(this.hideParent){
11183             this.parentEl = Roo.get(renderTo);
11184         }
11185         if(!this.dhconfig){
11186             if(!this.template){
11187                 if(!Roo.Button.buttonTemplate){
11188                     // hideous table template
11189                     Roo.Button.buttonTemplate = new Roo.Template(
11190                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11191                         '<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>',
11192                         "</tr></tbody></table>");
11193                 }
11194                 this.template = Roo.Button.buttonTemplate;
11195             }
11196             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11197             var btnEl = btn.child("button:first");
11198             btnEl.on('focus', this.onFocus, this);
11199             btnEl.on('blur', this.onBlur, this);
11200             if(this.cls){
11201                 btn.addClass(this.cls);
11202             }
11203             if(this.icon){
11204                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11205             }
11206             if(this.iconCls){
11207                 btnEl.addClass(this.iconCls);
11208                 if(!this.cls){
11209                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11210                 }
11211             }
11212             if(this.tabIndex !== undefined){
11213                 btnEl.dom.tabIndex = this.tabIndex;
11214             }
11215             if(this.tooltip){
11216                 if(typeof this.tooltip == 'object'){
11217                     Roo.QuickTips.tips(Roo.apply({
11218                           target: btnEl.id
11219                     }, this.tooltip));
11220                 } else {
11221                     btnEl.dom[this.tooltipType] = this.tooltip;
11222                 }
11223             }
11224         }else{
11225             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11226         }
11227         this.el = btn;
11228         if(this.id){
11229             this.el.dom.id = this.el.id = this.id;
11230         }
11231         if(this.menu){
11232             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11233             this.menu.on("show", this.onMenuShow, this);
11234             this.menu.on("hide", this.onMenuHide, this);
11235         }
11236         btn.addClass("x-btn");
11237         if(Roo.isIE && !Roo.isIE7){
11238             this.autoWidth.defer(1, this);
11239         }else{
11240             this.autoWidth();
11241         }
11242         if(this.handleMouseEvents){
11243             btn.on("mouseover", this.onMouseOver, this);
11244             btn.on("mouseout", this.onMouseOut, this);
11245             btn.on("mousedown", this.onMouseDown, this);
11246         }
11247         btn.on(this.clickEvent, this.onClick, this);
11248         //btn.on("mouseup", this.onMouseUp, this);
11249         if(this.hidden){
11250             this.hide();
11251         }
11252         if(this.disabled){
11253             this.disable();
11254         }
11255         Roo.ButtonToggleMgr.register(this);
11256         if(this.pressed){
11257             this.el.addClass("x-btn-pressed");
11258         }
11259         if(this.repeat){
11260             var repeater = new Roo.util.ClickRepeater(btn,
11261                 typeof this.repeat == "object" ? this.repeat : {}
11262             );
11263             repeater.on("click", this.onClick,  this);
11264         }
11265         
11266         this.fireEvent('render', this);
11267         
11268     },
11269     /**
11270      * Returns the button's underlying element
11271      * @return {Roo.Element} The element
11272      */
11273     getEl : function(){
11274         return this.el;  
11275     },
11276     
11277     /**
11278      * Destroys this Button and removes any listeners.
11279      */
11280     destroy : function(){
11281         Roo.ButtonToggleMgr.unregister(this);
11282         this.el.removeAllListeners();
11283         this.purgeListeners();
11284         this.el.remove();
11285     },
11286
11287     // private
11288     autoWidth : function(){
11289         if(this.el){
11290             this.el.setWidth("auto");
11291             if(Roo.isIE7 && Roo.isStrict){
11292                 var ib = this.el.child('button');
11293                 if(ib && ib.getWidth() > 20){
11294                     ib.clip();
11295                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11296                 }
11297             }
11298             if(this.minWidth){
11299                 if(this.hidden){
11300                     this.el.beginMeasure();
11301                 }
11302                 if(this.el.getWidth() < this.minWidth){
11303                     this.el.setWidth(this.minWidth);
11304                 }
11305                 if(this.hidden){
11306                     this.el.endMeasure();
11307                 }
11308             }
11309         }
11310     },
11311
11312     /**
11313      * Assigns this button's click handler
11314      * @param {Function} handler The function to call when the button is clicked
11315      * @param {Object} scope (optional) Scope for the function passed in
11316      */
11317     setHandler : function(handler, scope){
11318         this.handler = handler;
11319         this.scope = scope;  
11320     },
11321     
11322     /**
11323      * Sets this button's text
11324      * @param {String} text The button text
11325      */
11326     setText : function(text){
11327         this.text = text;
11328         if(this.el){
11329             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11330         }
11331         this.autoWidth();
11332     },
11333     
11334     /**
11335      * Gets the text for this button
11336      * @return {String} The button text
11337      */
11338     getText : function(){
11339         return this.text;  
11340     },
11341     
11342     /**
11343      * Show this button
11344      */
11345     show: function(){
11346         this.hidden = false;
11347         if(this.el){
11348             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11349         }
11350     },
11351     
11352     /**
11353      * Hide this button
11354      */
11355     hide: function(){
11356         this.hidden = true;
11357         if(this.el){
11358             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11359         }
11360     },
11361     
11362     /**
11363      * Convenience function for boolean show/hide
11364      * @param {Boolean} visible True to show, false to hide
11365      */
11366     setVisible: function(visible){
11367         if(visible) {
11368             this.show();
11369         }else{
11370             this.hide();
11371         }
11372     },
11373     
11374     /**
11375      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11376      * @param {Boolean} state (optional) Force a particular state
11377      */
11378     toggle : function(state){
11379         state = state === undefined ? !this.pressed : state;
11380         if(state != this.pressed){
11381             if(state){
11382                 this.el.addClass("x-btn-pressed");
11383                 this.pressed = true;
11384                 this.fireEvent("toggle", this, true);
11385             }else{
11386                 this.el.removeClass("x-btn-pressed");
11387                 this.pressed = false;
11388                 this.fireEvent("toggle", this, false);
11389             }
11390             if(this.toggleHandler){
11391                 this.toggleHandler.call(this.scope || this, this, state);
11392             }
11393         }
11394     },
11395     
11396     /**
11397      * Focus the button
11398      */
11399     focus : function(){
11400         this.el.child('button:first').focus();
11401     },
11402     
11403     /**
11404      * Disable this button
11405      */
11406     disable : function(){
11407         if(this.el){
11408             this.el.addClass("x-btn-disabled");
11409         }
11410         this.disabled = true;
11411     },
11412     
11413     /**
11414      * Enable this button
11415      */
11416     enable : function(){
11417         if(this.el){
11418             this.el.removeClass("x-btn-disabled");
11419         }
11420         this.disabled = false;
11421     },
11422
11423     /**
11424      * Convenience function for boolean enable/disable
11425      * @param {Boolean} enabled True to enable, false to disable
11426      */
11427     setDisabled : function(v){
11428         this[v !== true ? "enable" : "disable"]();
11429     },
11430
11431     // private
11432     onClick : function(e){
11433         if(e){
11434             e.preventDefault();
11435         }
11436         if(e.button != 0){
11437             return;
11438         }
11439         if(!this.disabled){
11440             if(this.enableToggle){
11441                 this.toggle();
11442             }
11443             if(this.menu && !this.menu.isVisible()){
11444                 this.menu.show(this.el, this.menuAlign);
11445             }
11446             this.fireEvent("click", this, e);
11447             if(this.handler){
11448                 this.el.removeClass("x-btn-over");
11449                 this.handler.call(this.scope || this, this, e);
11450             }
11451         }
11452     },
11453     // private
11454     onMouseOver : function(e){
11455         if(!this.disabled){
11456             this.el.addClass("x-btn-over");
11457             this.fireEvent('mouseover', this, e);
11458         }
11459     },
11460     // private
11461     onMouseOut : function(e){
11462         if(!e.within(this.el,  true)){
11463             this.el.removeClass("x-btn-over");
11464             this.fireEvent('mouseout', this, e);
11465         }
11466     },
11467     // private
11468     onFocus : function(e){
11469         if(!this.disabled){
11470             this.el.addClass("x-btn-focus");
11471         }
11472     },
11473     // private
11474     onBlur : function(e){
11475         this.el.removeClass("x-btn-focus");
11476     },
11477     // private
11478     onMouseDown : function(e){
11479         if(!this.disabled && e.button == 0){
11480             this.el.addClass("x-btn-click");
11481             Roo.get(document).on('mouseup', this.onMouseUp, this);
11482         }
11483     },
11484     // private
11485     onMouseUp : function(e){
11486         if(e.button == 0){
11487             this.el.removeClass("x-btn-click");
11488             Roo.get(document).un('mouseup', this.onMouseUp, this);
11489         }
11490     },
11491     // private
11492     onMenuShow : function(e){
11493         this.el.addClass("x-btn-menu-active");
11494     },
11495     // private
11496     onMenuHide : function(e){
11497         this.el.removeClass("x-btn-menu-active");
11498     }   
11499 });
11500
11501 // Private utility class used by Button
11502 Roo.ButtonToggleMgr = function(){
11503    var groups = {};
11504    
11505    function toggleGroup(btn, state){
11506        if(state){
11507            var g = groups[btn.toggleGroup];
11508            for(var i = 0, l = g.length; i < l; i++){
11509                if(g[i] != btn){
11510                    g[i].toggle(false);
11511                }
11512            }
11513        }
11514    }
11515    
11516    return {
11517        register : function(btn){
11518            if(!btn.toggleGroup){
11519                return;
11520            }
11521            var g = groups[btn.toggleGroup];
11522            if(!g){
11523                g = groups[btn.toggleGroup] = [];
11524            }
11525            g.push(btn);
11526            btn.on("toggle", toggleGroup);
11527        },
11528        
11529        unregister : function(btn){
11530            if(!btn.toggleGroup){
11531                return;
11532            }
11533            var g = groups[btn.toggleGroup];
11534            if(g){
11535                g.remove(btn);
11536                btn.un("toggle", toggleGroup);
11537            }
11538        }
11539    };
11540 }();/*
11541  * Based on:
11542  * Ext JS Library 1.1.1
11543  * Copyright(c) 2006-2007, Ext JS, LLC.
11544  *
11545  * Originally Released Under LGPL - original licence link has changed is not relivant.
11546  *
11547  * Fork - LGPL
11548  * <script type="text/javascript">
11549  */
11550  
11551 /**
11552  * @class Roo.SplitButton
11553  * @extends Roo.Button
11554  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
11555  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
11556  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
11557  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
11558  * @cfg {String} arrowTooltip The title attribute of the arrow
11559  * @constructor
11560  * Create a new menu button
11561  * @param {String/HTMLElement/Element} renderTo The element to append the button to
11562  * @param {Object} config The config object
11563  */
11564 Roo.SplitButton = function(renderTo, config){
11565     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
11566     /**
11567      * @event arrowclick
11568      * Fires when this button's arrow is clicked
11569      * @param {SplitButton} this
11570      * @param {EventObject} e The click event
11571      */
11572     this.addEvents({"arrowclick":true});
11573 };
11574
11575 Roo.extend(Roo.SplitButton, Roo.Button, {
11576     render : function(renderTo){
11577         // this is one sweet looking template!
11578         var tpl = new Roo.Template(
11579             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
11580             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
11581             '<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>',
11582             "</tbody></table></td><td>",
11583             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
11584             '<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>',
11585             "</tbody></table></td></tr></table>"
11586         );
11587         var btn = tpl.append(renderTo, [this.text, this.type], true);
11588         var btnEl = btn.child("button");
11589         if(this.cls){
11590             btn.addClass(this.cls);
11591         }
11592         if(this.icon){
11593             btnEl.setStyle('background-image', 'url(' +this.icon +')');
11594         }
11595         if(this.iconCls){
11596             btnEl.addClass(this.iconCls);
11597             if(!this.cls){
11598                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11599             }
11600         }
11601         this.el = btn;
11602         if(this.handleMouseEvents){
11603             btn.on("mouseover", this.onMouseOver, this);
11604             btn.on("mouseout", this.onMouseOut, this);
11605             btn.on("mousedown", this.onMouseDown, this);
11606             btn.on("mouseup", this.onMouseUp, this);
11607         }
11608         btn.on(this.clickEvent, this.onClick, this);
11609         if(this.tooltip){
11610             if(typeof this.tooltip == 'object'){
11611                 Roo.QuickTips.tips(Roo.apply({
11612                       target: btnEl.id
11613                 }, this.tooltip));
11614             } else {
11615                 btnEl.dom[this.tooltipType] = this.tooltip;
11616             }
11617         }
11618         if(this.arrowTooltip){
11619             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
11620         }
11621         if(this.hidden){
11622             this.hide();
11623         }
11624         if(this.disabled){
11625             this.disable();
11626         }
11627         if(this.pressed){
11628             this.el.addClass("x-btn-pressed");
11629         }
11630         if(Roo.isIE && !Roo.isIE7){
11631             this.autoWidth.defer(1, this);
11632         }else{
11633             this.autoWidth();
11634         }
11635         if(this.menu){
11636             this.menu.on("show", this.onMenuShow, this);
11637             this.menu.on("hide", this.onMenuHide, this);
11638         }
11639         this.fireEvent('render', this);
11640     },
11641
11642     // private
11643     autoWidth : function(){
11644         if(this.el){
11645             var tbl = this.el.child("table:first");
11646             var tbl2 = this.el.child("table:last");
11647             this.el.setWidth("auto");
11648             tbl.setWidth("auto");
11649             if(Roo.isIE7 && Roo.isStrict){
11650                 var ib = this.el.child('button:first');
11651                 if(ib && ib.getWidth() > 20){
11652                     ib.clip();
11653                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11654                 }
11655             }
11656             if(this.minWidth){
11657                 if(this.hidden){
11658                     this.el.beginMeasure();
11659                 }
11660                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
11661                     tbl.setWidth(this.minWidth-tbl2.getWidth());
11662                 }
11663                 if(this.hidden){
11664                     this.el.endMeasure();
11665                 }
11666             }
11667             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
11668         } 
11669     },
11670     /**
11671      * Sets this button's click handler
11672      * @param {Function} handler The function to call when the button is clicked
11673      * @param {Object} scope (optional) Scope for the function passed above
11674      */
11675     setHandler : function(handler, scope){
11676         this.handler = handler;
11677         this.scope = scope;  
11678     },
11679     
11680     /**
11681      * Sets this button's arrow click handler
11682      * @param {Function} handler The function to call when the arrow is clicked
11683      * @param {Object} scope (optional) Scope for the function passed above
11684      */
11685     setArrowHandler : function(handler, scope){
11686         this.arrowHandler = handler;
11687         this.scope = scope;  
11688     },
11689     
11690     /**
11691      * Focus the button
11692      */
11693     focus : function(){
11694         if(this.el){
11695             this.el.child("button:first").focus();
11696         }
11697     },
11698
11699     // private
11700     onClick : function(e){
11701         e.preventDefault();
11702         if(!this.disabled){
11703             if(e.getTarget(".x-btn-menu-arrow-wrap")){
11704                 if(this.menu && !this.menu.isVisible()){
11705                     this.menu.show(this.el, this.menuAlign);
11706                 }
11707                 this.fireEvent("arrowclick", this, e);
11708                 if(this.arrowHandler){
11709                     this.arrowHandler.call(this.scope || this, this, e);
11710                 }
11711             }else{
11712                 this.fireEvent("click", this, e);
11713                 if(this.handler){
11714                     this.handler.call(this.scope || this, this, e);
11715                 }
11716             }
11717         }
11718     },
11719     // private
11720     onMouseDown : function(e){
11721         if(!this.disabled){
11722             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
11723         }
11724     },
11725     // private
11726     onMouseUp : function(e){
11727         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
11728     }   
11729 });
11730
11731
11732 // backwards compat
11733 Roo.MenuButton = Roo.SplitButton;/*
11734  * Based on:
11735  * Ext JS Library 1.1.1
11736  * Copyright(c) 2006-2007, Ext JS, LLC.
11737  *
11738  * Originally Released Under LGPL - original licence link has changed is not relivant.
11739  *
11740  * Fork - LGPL
11741  * <script type="text/javascript">
11742  */
11743
11744 /**
11745  * @class Roo.Toolbar
11746  * Basic Toolbar class.
11747  * @constructor
11748  * Creates a new Toolbar
11749  * @param {Object} container The config object
11750  */ 
11751 Roo.Toolbar = function(container, buttons, config)
11752 {
11753     /// old consturctor format still supported..
11754     if(container instanceof Array){ // omit the container for later rendering
11755         buttons = container;
11756         config = buttons;
11757         container = null;
11758     }
11759     if (typeof(container) == 'object' && container.xtype) {
11760         config = container;
11761         container = config.container;
11762         buttons = config.buttons || []; // not really - use items!!
11763     }
11764     var xitems = [];
11765     if (config && config.items) {
11766         xitems = config.items;
11767         delete config.items;
11768     }
11769     Roo.apply(this, config);
11770     this.buttons = buttons;
11771     
11772     if(container){
11773         this.render(container);
11774     }
11775     this.xitems = xitems;
11776     Roo.each(xitems, function(b) {
11777         this.add(b);
11778     }, this);
11779     
11780 };
11781
11782 Roo.Toolbar.prototype = {
11783     /**
11784      * @cfg {Array} items
11785      * array of button configs or elements to add (will be converted to a MixedCollection)
11786      */
11787     
11788     /**
11789      * @cfg {String/HTMLElement/Element} container
11790      * The id or element that will contain the toolbar
11791      */
11792     // private
11793     render : function(ct){
11794         this.el = Roo.get(ct);
11795         if(this.cls){
11796             this.el.addClass(this.cls);
11797         }
11798         // using a table allows for vertical alignment
11799         // 100% width is needed by Safari...
11800         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
11801         this.tr = this.el.child("tr", true);
11802         var autoId = 0;
11803         this.items = new Roo.util.MixedCollection(false, function(o){
11804             return o.id || ("item" + (++autoId));
11805         });
11806         if(this.buttons){
11807             this.add.apply(this, this.buttons);
11808             delete this.buttons;
11809         }
11810     },
11811
11812     /**
11813      * Adds element(s) to the toolbar -- this function takes a variable number of 
11814      * arguments of mixed type and adds them to the toolbar.
11815      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
11816      * <ul>
11817      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
11818      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
11819      * <li>Field: Any form field (equivalent to {@link #addField})</li>
11820      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
11821      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
11822      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
11823      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
11824      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
11825      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
11826      * </ul>
11827      * @param {Mixed} arg2
11828      * @param {Mixed} etc.
11829      */
11830     add : function(){
11831         var a = arguments, l = a.length;
11832         for(var i = 0; i < l; i++){
11833             this._add(a[i]);
11834         }
11835     },
11836     // private..
11837     _add : function(el) {
11838         
11839         if (el.xtype) {
11840             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
11841         }
11842         
11843         if (el.applyTo){ // some kind of form field
11844             return this.addField(el);
11845         } 
11846         if (el.render){ // some kind of Toolbar.Item
11847             return this.addItem(el);
11848         }
11849         if (typeof el == "string"){ // string
11850             if(el == "separator" || el == "-"){
11851                 return this.addSeparator();
11852             }
11853             if (el == " "){
11854                 return this.addSpacer();
11855             }
11856             if(el == "->"){
11857                 return this.addFill();
11858             }
11859             return this.addText(el);
11860             
11861         }
11862         if(el.tagName){ // element
11863             return this.addElement(el);
11864         }
11865         if(typeof el == "object"){ // must be button config?
11866             return this.addButton(el);
11867         }
11868         // and now what?!?!
11869         return false;
11870         
11871     },
11872     
11873     /**
11874      * Add an Xtype element
11875      * @param {Object} xtype Xtype Object
11876      * @return {Object} created Object
11877      */
11878     addxtype : function(e){
11879         return this.add(e);  
11880     },
11881     
11882     /**
11883      * Returns the Element for this toolbar.
11884      * @return {Roo.Element}
11885      */
11886     getEl : function(){
11887         return this.el;  
11888     },
11889     
11890     /**
11891      * Adds a separator
11892      * @return {Roo.Toolbar.Item} The separator item
11893      */
11894     addSeparator : function(){
11895         return this.addItem(new Roo.Toolbar.Separator());
11896     },
11897
11898     /**
11899      * Adds a spacer element
11900      * @return {Roo.Toolbar.Spacer} The spacer item
11901      */
11902     addSpacer : function(){
11903         return this.addItem(new Roo.Toolbar.Spacer());
11904     },
11905
11906     /**
11907      * Adds a fill element that forces subsequent additions to the right side of the toolbar
11908      * @return {Roo.Toolbar.Fill} The fill item
11909      */
11910     addFill : function(){
11911         return this.addItem(new Roo.Toolbar.Fill());
11912     },
11913
11914     /**
11915      * Adds any standard HTML element to the toolbar
11916      * @param {String/HTMLElement/Element} el The element or id of the element to add
11917      * @return {Roo.Toolbar.Item} The element's item
11918      */
11919     addElement : function(el){
11920         return this.addItem(new Roo.Toolbar.Item(el));
11921     },
11922     /**
11923      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
11924      * @type Roo.util.MixedCollection  
11925      */
11926     items : false,
11927      
11928     /**
11929      * Adds any Toolbar.Item or subclass
11930      * @param {Roo.Toolbar.Item} item
11931      * @return {Roo.Toolbar.Item} The item
11932      */
11933     addItem : function(item){
11934         var td = this.nextBlock();
11935         item.render(td);
11936         this.items.add(item);
11937         return item;
11938     },
11939     
11940     /**
11941      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
11942      * @param {Object/Array} config A button config or array of configs
11943      * @return {Roo.Toolbar.Button/Array}
11944      */
11945     addButton : function(config){
11946         if(config instanceof Array){
11947             var buttons = [];
11948             for(var i = 0, len = config.length; i < len; i++) {
11949                 buttons.push(this.addButton(config[i]));
11950             }
11951             return buttons;
11952         }
11953         var b = config;
11954         if(!(config instanceof Roo.Toolbar.Button)){
11955             b = config.split ?
11956                 new Roo.Toolbar.SplitButton(config) :
11957                 new Roo.Toolbar.Button(config);
11958         }
11959         var td = this.nextBlock();
11960         b.render(td);
11961         this.items.add(b);
11962         return b;
11963     },
11964     
11965     /**
11966      * Adds text to the toolbar
11967      * @param {String} text The text to add
11968      * @return {Roo.Toolbar.Item} The element's item
11969      */
11970     addText : function(text){
11971         return this.addItem(new Roo.Toolbar.TextItem(text));
11972     },
11973     
11974     /**
11975      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
11976      * @param {Number} index The index where the item is to be inserted
11977      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
11978      * @return {Roo.Toolbar.Button/Item}
11979      */
11980     insertButton : function(index, item){
11981         if(item instanceof Array){
11982             var buttons = [];
11983             for(var i = 0, len = item.length; i < len; i++) {
11984                buttons.push(this.insertButton(index + i, item[i]));
11985             }
11986             return buttons;
11987         }
11988         if (!(item instanceof Roo.Toolbar.Button)){
11989            item = new Roo.Toolbar.Button(item);
11990         }
11991         var td = document.createElement("td");
11992         this.tr.insertBefore(td, this.tr.childNodes[index]);
11993         item.render(td);
11994         this.items.insert(index, item);
11995         return item;
11996     },
11997     
11998     /**
11999      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12000      * @param {Object} config
12001      * @return {Roo.Toolbar.Item} The element's item
12002      */
12003     addDom : function(config, returnEl){
12004         var td = this.nextBlock();
12005         Roo.DomHelper.overwrite(td, config);
12006         var ti = new Roo.Toolbar.Item(td.firstChild);
12007         ti.render(td);
12008         this.items.add(ti);
12009         return ti;
12010     },
12011
12012     /**
12013      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12014      * @type Roo.util.MixedCollection  
12015      */
12016     fields : false,
12017     
12018     /**
12019      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12020      * Note: the field should not have been rendered yet. For a field that has already been
12021      * rendered, use {@link #addElement}.
12022      * @param {Roo.form.Field} field
12023      * @return {Roo.ToolbarItem}
12024      */
12025      
12026       
12027     addField : function(field) {
12028         if (!this.fields) {
12029             var autoId = 0;
12030             this.fields = new Roo.util.MixedCollection(false, function(o){
12031                 return o.id || ("item" + (++autoId));
12032             });
12033
12034         }
12035         
12036         var td = this.nextBlock();
12037         field.render(td);
12038         var ti = new Roo.Toolbar.Item(td.firstChild);
12039         ti.render(td);
12040         this.items.add(ti);
12041         this.fields.add(field);
12042         return ti;
12043     },
12044     /**
12045      * Hide the toolbar
12046      * @method hide
12047      */
12048      
12049       
12050     hide : function()
12051     {
12052         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12053         this.el.child('div').hide();
12054     },
12055     /**
12056      * Show the toolbar
12057      * @method show
12058      */
12059     show : function()
12060     {
12061         this.el.child('div').show();
12062     },
12063       
12064     // private
12065     nextBlock : function(){
12066         var td = document.createElement("td");
12067         this.tr.appendChild(td);
12068         return td;
12069     },
12070
12071     // private
12072     destroy : function(){
12073         if(this.items){ // rendered?
12074             Roo.destroy.apply(Roo, this.items.items);
12075         }
12076         if(this.fields){ // rendered?
12077             Roo.destroy.apply(Roo, this.fields.items);
12078         }
12079         Roo.Element.uncache(this.el, this.tr);
12080     }
12081 };
12082
12083 /**
12084  * @class Roo.Toolbar.Item
12085  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12086  * @constructor
12087  * Creates a new Item
12088  * @param {HTMLElement} el 
12089  */
12090 Roo.Toolbar.Item = function(el){
12091     this.el = Roo.getDom(el);
12092     this.id = Roo.id(this.el);
12093     this.hidden = false;
12094 };
12095
12096 Roo.Toolbar.Item.prototype = {
12097     
12098     /**
12099      * Get this item's HTML Element
12100      * @return {HTMLElement}
12101      */
12102     getEl : function(){
12103        return this.el;  
12104     },
12105
12106     // private
12107     render : function(td){
12108         this.td = td;
12109         td.appendChild(this.el);
12110     },
12111     
12112     /**
12113      * Removes and destroys this item.
12114      */
12115     destroy : function(){
12116         this.td.parentNode.removeChild(this.td);
12117     },
12118     
12119     /**
12120      * Shows this item.
12121      */
12122     show: function(){
12123         this.hidden = false;
12124         this.td.style.display = "";
12125     },
12126     
12127     /**
12128      * Hides this item.
12129      */
12130     hide: function(){
12131         this.hidden = true;
12132         this.td.style.display = "none";
12133     },
12134     
12135     /**
12136      * Convenience function for boolean show/hide.
12137      * @param {Boolean} visible true to show/false to hide
12138      */
12139     setVisible: function(visible){
12140         if(visible) {
12141             this.show();
12142         }else{
12143             this.hide();
12144         }
12145     },
12146     
12147     /**
12148      * Try to focus this item.
12149      */
12150     focus : function(){
12151         Roo.fly(this.el).focus();
12152     },
12153     
12154     /**
12155      * Disables this item.
12156      */
12157     disable : function(){
12158         Roo.fly(this.td).addClass("x-item-disabled");
12159         this.disabled = true;
12160         this.el.disabled = true;
12161     },
12162     
12163     /**
12164      * Enables this item.
12165      */
12166     enable : function(){
12167         Roo.fly(this.td).removeClass("x-item-disabled");
12168         this.disabled = false;
12169         this.el.disabled = false;
12170     }
12171 };
12172
12173
12174 /**
12175  * @class Roo.Toolbar.Separator
12176  * @extends Roo.Toolbar.Item
12177  * A simple toolbar separator class
12178  * @constructor
12179  * Creates a new Separator
12180  */
12181 Roo.Toolbar.Separator = function(){
12182     var s = document.createElement("span");
12183     s.className = "ytb-sep";
12184     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12185 };
12186 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12187     enable:Roo.emptyFn,
12188     disable:Roo.emptyFn,
12189     focus:Roo.emptyFn
12190 });
12191
12192 /**
12193  * @class Roo.Toolbar.Spacer
12194  * @extends Roo.Toolbar.Item
12195  * A simple element that adds extra horizontal space to a toolbar.
12196  * @constructor
12197  * Creates a new Spacer
12198  */
12199 Roo.Toolbar.Spacer = function(){
12200     var s = document.createElement("div");
12201     s.className = "ytb-spacer";
12202     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12203 };
12204 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12205     enable:Roo.emptyFn,
12206     disable:Roo.emptyFn,
12207     focus:Roo.emptyFn
12208 });
12209
12210 /**
12211  * @class Roo.Toolbar.Fill
12212  * @extends Roo.Toolbar.Spacer
12213  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12214  * @constructor
12215  * Creates a new Spacer
12216  */
12217 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12218     // private
12219     render : function(td){
12220         td.style.width = '100%';
12221         Roo.Toolbar.Fill.superclass.render.call(this, td);
12222     }
12223 });
12224
12225 /**
12226  * @class Roo.Toolbar.TextItem
12227  * @extends Roo.Toolbar.Item
12228  * A simple class that renders text directly into a toolbar.
12229  * @constructor
12230  * Creates a new TextItem
12231  * @param {String} text
12232  */
12233 Roo.Toolbar.TextItem = function(text){
12234     if (typeof(text) == 'object') {
12235         text = text.text;
12236     }
12237     var s = document.createElement("span");
12238     s.className = "ytb-text";
12239     s.innerHTML = text;
12240     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12241 };
12242 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12243     enable:Roo.emptyFn,
12244     disable:Roo.emptyFn,
12245     focus:Roo.emptyFn
12246 });
12247
12248 /**
12249  * @class Roo.Toolbar.Button
12250  * @extends Roo.Button
12251  * A button that renders into a toolbar.
12252  * @constructor
12253  * Creates a new Button
12254  * @param {Object} config A standard {@link Roo.Button} config object
12255  */
12256 Roo.Toolbar.Button = function(config){
12257     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12258 };
12259 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12260     render : function(td){
12261         this.td = td;
12262         Roo.Toolbar.Button.superclass.render.call(this, td);
12263     },
12264     
12265     /**
12266      * Removes and destroys this button
12267      */
12268     destroy : function(){
12269         Roo.Toolbar.Button.superclass.destroy.call(this);
12270         this.td.parentNode.removeChild(this.td);
12271     },
12272     
12273     /**
12274      * Shows this button
12275      */
12276     show: function(){
12277         this.hidden = false;
12278         this.td.style.display = "";
12279     },
12280     
12281     /**
12282      * Hides this button
12283      */
12284     hide: function(){
12285         this.hidden = true;
12286         this.td.style.display = "none";
12287     },
12288
12289     /**
12290      * Disables this item
12291      */
12292     disable : function(){
12293         Roo.fly(this.td).addClass("x-item-disabled");
12294         this.disabled = true;
12295     },
12296
12297     /**
12298      * Enables this item
12299      */
12300     enable : function(){
12301         Roo.fly(this.td).removeClass("x-item-disabled");
12302         this.disabled = false;
12303     }
12304 });
12305 // backwards compat
12306 Roo.ToolbarButton = Roo.Toolbar.Button;
12307
12308 /**
12309  * @class Roo.Toolbar.SplitButton
12310  * @extends Roo.SplitButton
12311  * A menu button that renders into a toolbar.
12312  * @constructor
12313  * Creates a new SplitButton
12314  * @param {Object} config A standard {@link Roo.SplitButton} config object
12315  */
12316 Roo.Toolbar.SplitButton = function(config){
12317     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12318 };
12319 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12320     render : function(td){
12321         this.td = td;
12322         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12323     },
12324     
12325     /**
12326      * Removes and destroys this button
12327      */
12328     destroy : function(){
12329         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12330         this.td.parentNode.removeChild(this.td);
12331     },
12332     
12333     /**
12334      * Shows this button
12335      */
12336     show: function(){
12337         this.hidden = false;
12338         this.td.style.display = "";
12339     },
12340     
12341     /**
12342      * Hides this button
12343      */
12344     hide: function(){
12345         this.hidden = true;
12346         this.td.style.display = "none";
12347     }
12348 });
12349
12350 // backwards compat
12351 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12352  * Based on:
12353  * Ext JS Library 1.1.1
12354  * Copyright(c) 2006-2007, Ext JS, LLC.
12355  *
12356  * Originally Released Under LGPL - original licence link has changed is not relivant.
12357  *
12358  * Fork - LGPL
12359  * <script type="text/javascript">
12360  */
12361  
12362 /**
12363  * @class Roo.PagingToolbar
12364  * @extends Roo.Toolbar
12365  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12366  * @constructor
12367  * Create a new PagingToolbar
12368  * @param {Object} config The config object
12369  */
12370 Roo.PagingToolbar = function(el, ds, config)
12371 {
12372     // old args format still supported... - xtype is prefered..
12373     if (typeof(el) == 'object' && el.xtype) {
12374         // created from xtype...
12375         config = el;
12376         ds = el.dataSource;
12377         el = config.container;
12378     }
12379     var items = [];
12380     if (config.items) {
12381         items = config.items;
12382         config.items = [];
12383     }
12384     
12385     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12386     this.ds = ds;
12387     this.cursor = 0;
12388     this.renderButtons(this.el);
12389     this.bind(ds);
12390     
12391     // supprot items array.
12392    
12393     Roo.each(items, function(e) {
12394         this.add(Roo.factory(e));
12395     },this);
12396     
12397 };
12398
12399 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12400     /**
12401      * @cfg {Roo.data.Store} dataSource
12402      * The underlying data store providing the paged data
12403      */
12404     /**
12405      * @cfg {String/HTMLElement/Element} container
12406      * container The id or element that will contain the toolbar
12407      */
12408     /**
12409      * @cfg {Boolean} displayInfo
12410      * True to display the displayMsg (defaults to false)
12411      */
12412     /**
12413      * @cfg {Number} pageSize
12414      * The number of records to display per page (defaults to 20)
12415      */
12416     pageSize: 20,
12417     /**
12418      * @cfg {String} displayMsg
12419      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12420      */
12421     displayMsg : 'Displaying {0} - {1} of {2}',
12422     /**
12423      * @cfg {String} emptyMsg
12424      * The message to display when no records are found (defaults to "No data to display")
12425      */
12426     emptyMsg : 'No data to display',
12427     /**
12428      * Customizable piece of the default paging text (defaults to "Page")
12429      * @type String
12430      */
12431     beforePageText : "Page",
12432     /**
12433      * Customizable piece of the default paging text (defaults to "of %0")
12434      * @type String
12435      */
12436     afterPageText : "of {0}",
12437     /**
12438      * Customizable piece of the default paging text (defaults to "First Page")
12439      * @type String
12440      */
12441     firstText : "First Page",
12442     /**
12443      * Customizable piece of the default paging text (defaults to "Previous Page")
12444      * @type String
12445      */
12446     prevText : "Previous Page",
12447     /**
12448      * Customizable piece of the default paging text (defaults to "Next Page")
12449      * @type String
12450      */
12451     nextText : "Next Page",
12452     /**
12453      * Customizable piece of the default paging text (defaults to "Last Page")
12454      * @type String
12455      */
12456     lastText : "Last Page",
12457     /**
12458      * Customizable piece of the default paging text (defaults to "Refresh")
12459      * @type String
12460      */
12461     refreshText : "Refresh",
12462
12463     // private
12464     renderButtons : function(el){
12465         Roo.PagingToolbar.superclass.render.call(this, el);
12466         this.first = this.addButton({
12467             tooltip: this.firstText,
12468             cls: "x-btn-icon x-grid-page-first",
12469             disabled: true,
12470             handler: this.onClick.createDelegate(this, ["first"])
12471         });
12472         this.prev = this.addButton({
12473             tooltip: this.prevText,
12474             cls: "x-btn-icon x-grid-page-prev",
12475             disabled: true,
12476             handler: this.onClick.createDelegate(this, ["prev"])
12477         });
12478         //this.addSeparator();
12479         this.add(this.beforePageText);
12480         this.field = Roo.get(this.addDom({
12481            tag: "input",
12482            type: "text",
12483            size: "3",
12484            value: "1",
12485            cls: "x-grid-page-number"
12486         }).el);
12487         this.field.on("keydown", this.onPagingKeydown, this);
12488         this.field.on("focus", function(){this.dom.select();});
12489         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12490         this.field.setHeight(18);
12491         //this.addSeparator();
12492         this.next = this.addButton({
12493             tooltip: this.nextText,
12494             cls: "x-btn-icon x-grid-page-next",
12495             disabled: true,
12496             handler: this.onClick.createDelegate(this, ["next"])
12497         });
12498         this.last = this.addButton({
12499             tooltip: this.lastText,
12500             cls: "x-btn-icon x-grid-page-last",
12501             disabled: true,
12502             handler: this.onClick.createDelegate(this, ["last"])
12503         });
12504         //this.addSeparator();
12505         this.loading = this.addButton({
12506             tooltip: this.refreshText,
12507             cls: "x-btn-icon x-grid-loading",
12508             handler: this.onClick.createDelegate(this, ["refresh"])
12509         });
12510
12511         if(this.displayInfo){
12512             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12513         }
12514     },
12515
12516     // private
12517     updateInfo : function(){
12518         if(this.displayEl){
12519             var count = this.ds.getCount();
12520             var msg = count == 0 ?
12521                 this.emptyMsg :
12522                 String.format(
12523                     this.displayMsg,
12524                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
12525                 );
12526             this.displayEl.update(msg);
12527         }
12528     },
12529
12530     // private
12531     onLoad : function(ds, r, o){
12532        this.cursor = o.params ? o.params.start : 0;
12533        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12534
12535        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
12536        this.field.dom.value = ap;
12537        this.first.setDisabled(ap == 1);
12538        this.prev.setDisabled(ap == 1);
12539        this.next.setDisabled(ap == ps);
12540        this.last.setDisabled(ap == ps);
12541        this.loading.enable();
12542        this.updateInfo();
12543     },
12544
12545     // private
12546     getPageData : function(){
12547         var total = this.ds.getTotalCount();
12548         return {
12549             total : total,
12550             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
12551             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
12552         };
12553     },
12554
12555     // private
12556     onLoadError : function(){
12557         this.loading.enable();
12558     },
12559
12560     // private
12561     onPagingKeydown : function(e){
12562         var k = e.getKey();
12563         var d = this.getPageData();
12564         if(k == e.RETURN){
12565             var v = this.field.dom.value, pageNum;
12566             if(!v || isNaN(pageNum = parseInt(v, 10))){
12567                 this.field.dom.value = d.activePage;
12568                 return;
12569             }
12570             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
12571             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12572             e.stopEvent();
12573         }
12574         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))
12575         {
12576           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
12577           this.field.dom.value = pageNum;
12578           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
12579           e.stopEvent();
12580         }
12581         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12582         {
12583           var v = this.field.dom.value, pageNum; 
12584           var increment = (e.shiftKey) ? 10 : 1;
12585           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12586             increment *= -1;
12587           if(!v || isNaN(pageNum = parseInt(v, 10))) {
12588             this.field.dom.value = d.activePage;
12589             return;
12590           }
12591           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
12592           {
12593             this.field.dom.value = parseInt(v, 10) + increment;
12594             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
12595             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12596           }
12597           e.stopEvent();
12598         }
12599     },
12600
12601     // private
12602     beforeLoad : function(){
12603         if(this.loading){
12604             this.loading.disable();
12605         }
12606     },
12607
12608     // private
12609     onClick : function(which){
12610         var ds = this.ds;
12611         switch(which){
12612             case "first":
12613                 ds.load({params:{start: 0, limit: this.pageSize}});
12614             break;
12615             case "prev":
12616                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
12617             break;
12618             case "next":
12619                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
12620             break;
12621             case "last":
12622                 var total = ds.getTotalCount();
12623                 var extra = total % this.pageSize;
12624                 var lastStart = extra ? (total - extra) : total-this.pageSize;
12625                 ds.load({params:{start: lastStart, limit: this.pageSize}});
12626             break;
12627             case "refresh":
12628                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
12629             break;
12630         }
12631     },
12632
12633     /**
12634      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
12635      * @param {Roo.data.Store} store The data store to unbind
12636      */
12637     unbind : function(ds){
12638         ds.un("beforeload", this.beforeLoad, this);
12639         ds.un("load", this.onLoad, this);
12640         ds.un("loadexception", this.onLoadError, this);
12641         ds.un("remove", this.updateInfo, this);
12642         ds.un("add", this.updateInfo, this);
12643         this.ds = undefined;
12644     },
12645
12646     /**
12647      * Binds the paging toolbar to the specified {@link Roo.data.Store}
12648      * @param {Roo.data.Store} store The data store to bind
12649      */
12650     bind : function(ds){
12651         ds.on("beforeload", this.beforeLoad, this);
12652         ds.on("load", this.onLoad, this);
12653         ds.on("loadexception", this.onLoadError, this);
12654         ds.on("remove", this.updateInfo, this);
12655         ds.on("add", this.updateInfo, this);
12656         this.ds = ds;
12657     }
12658 });/*
12659  * Based on:
12660  * Ext JS Library 1.1.1
12661  * Copyright(c) 2006-2007, Ext JS, LLC.
12662  *
12663  * Originally Released Under LGPL - original licence link has changed is not relivant.
12664  *
12665  * Fork - LGPL
12666  * <script type="text/javascript">
12667  */
12668
12669 /**
12670  * @class Roo.Resizable
12671  * @extends Roo.util.Observable
12672  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
12673  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
12674  * 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
12675  * the element will be wrapped for you automatically.</p>
12676  * <p>Here is the list of valid resize handles:</p>
12677  * <pre>
12678 Value   Description
12679 ------  -------------------
12680  'n'     north
12681  's'     south
12682  'e'     east
12683  'w'     west
12684  'nw'    northwest
12685  'sw'    southwest
12686  'se'    southeast
12687  'ne'    northeast
12688  'hd'    horizontal drag
12689  'all'   all
12690 </pre>
12691  * <p>Here's an example showing the creation of a typical Resizable:</p>
12692  * <pre><code>
12693 var resizer = new Roo.Resizable("element-id", {
12694     handles: 'all',
12695     minWidth: 200,
12696     minHeight: 100,
12697     maxWidth: 500,
12698     maxHeight: 400,
12699     pinned: true
12700 });
12701 resizer.on("resize", myHandler);
12702 </code></pre>
12703  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
12704  * resizer.east.setDisplayed(false);</p>
12705  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
12706  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
12707  * resize operation's new size (defaults to [0, 0])
12708  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
12709  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
12710  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
12711  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
12712  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
12713  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
12714  * @cfg {Number} width The width of the element in pixels (defaults to null)
12715  * @cfg {Number} height The height of the element in pixels (defaults to null)
12716  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
12717  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
12718  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
12719  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
12720  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
12721  * in favor of the handles config option (defaults to false)
12722  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
12723  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
12724  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
12725  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
12726  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
12727  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
12728  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
12729  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
12730  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
12731  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
12732  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
12733  * @constructor
12734  * Create a new resizable component
12735  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
12736  * @param {Object} config configuration options
12737   */
12738 Roo.Resizable = function(el, config)
12739 {
12740     this.el = Roo.get(el);
12741
12742     if(config && config.wrap){
12743         config.resizeChild = this.el;
12744         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
12745         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
12746         this.el.setStyle("overflow", "hidden");
12747         this.el.setPositioning(config.resizeChild.getPositioning());
12748         config.resizeChild.clearPositioning();
12749         if(!config.width || !config.height){
12750             var csize = config.resizeChild.getSize();
12751             this.el.setSize(csize.width, csize.height);
12752         }
12753         if(config.pinned && !config.adjustments){
12754             config.adjustments = "auto";
12755         }
12756     }
12757
12758     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
12759     this.proxy.unselectable();
12760     this.proxy.enableDisplayMode('block');
12761
12762     Roo.apply(this, config);
12763
12764     if(this.pinned){
12765         this.disableTrackOver = true;
12766         this.el.addClass("x-resizable-pinned");
12767     }
12768     // if the element isn't positioned, make it relative
12769     var position = this.el.getStyle("position");
12770     if(position != "absolute" && position != "fixed"){
12771         this.el.setStyle("position", "relative");
12772     }
12773     if(!this.handles){ // no handles passed, must be legacy style
12774         this.handles = 's,e,se';
12775         if(this.multiDirectional){
12776             this.handles += ',n,w';
12777         }
12778     }
12779     if(this.handles == "all"){
12780         this.handles = "n s e w ne nw se sw";
12781     }
12782     var hs = this.handles.split(/\s*?[,;]\s*?| /);
12783     var ps = Roo.Resizable.positions;
12784     for(var i = 0, len = hs.length; i < len; i++){
12785         if(hs[i] && ps[hs[i]]){
12786             var pos = ps[hs[i]];
12787             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
12788         }
12789     }
12790     // legacy
12791     this.corner = this.southeast;
12792     
12793     // updateBox = the box can move..
12794     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
12795         this.updateBox = true;
12796     }
12797
12798     this.activeHandle = null;
12799
12800     if(this.resizeChild){
12801         if(typeof this.resizeChild == "boolean"){
12802             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
12803         }else{
12804             this.resizeChild = Roo.get(this.resizeChild, true);
12805         }
12806     }
12807     
12808     if(this.adjustments == "auto"){
12809         var rc = this.resizeChild;
12810         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
12811         if(rc && (hw || hn)){
12812             rc.position("relative");
12813             rc.setLeft(hw ? hw.el.getWidth() : 0);
12814             rc.setTop(hn ? hn.el.getHeight() : 0);
12815         }
12816         this.adjustments = [
12817             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
12818             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
12819         ];
12820     }
12821
12822     if(this.draggable){
12823         this.dd = this.dynamic ?
12824             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
12825         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
12826     }
12827
12828     // public events
12829     this.addEvents({
12830         /**
12831          * @event beforeresize
12832          * Fired before resize is allowed. Set enabled to false to cancel resize.
12833          * @param {Roo.Resizable} this
12834          * @param {Roo.EventObject} e The mousedown event
12835          */
12836         "beforeresize" : true,
12837         /**
12838          * @event resizing
12839          * Fired a resizing.
12840          * @param {Roo.Resizable} this
12841          * @param {Number} x The new x position
12842          * @param {Number} y The new y position
12843          * @param {Number} w The new w width
12844          * @param {Number} h The new h hight
12845          * @param {Roo.EventObject} e The mouseup event
12846          */
12847         "resizing" : true,
12848         /**
12849          * @event resize
12850          * Fired after a resize.
12851          * @param {Roo.Resizable} this
12852          * @param {Number} width The new width
12853          * @param {Number} height The new height
12854          * @param {Roo.EventObject} e The mouseup event
12855          */
12856         "resize" : true
12857     });
12858
12859     if(this.width !== null && this.height !== null){
12860         this.resizeTo(this.width, this.height);
12861     }else{
12862         this.updateChildSize();
12863     }
12864     if(Roo.isIE){
12865         this.el.dom.style.zoom = 1;
12866     }
12867     Roo.Resizable.superclass.constructor.call(this);
12868 };
12869
12870 Roo.extend(Roo.Resizable, Roo.util.Observable, {
12871         resizeChild : false,
12872         adjustments : [0, 0],
12873         minWidth : 5,
12874         minHeight : 5,
12875         maxWidth : 10000,
12876         maxHeight : 10000,
12877         enabled : true,
12878         animate : false,
12879         duration : .35,
12880         dynamic : false,
12881         handles : false,
12882         multiDirectional : false,
12883         disableTrackOver : false,
12884         easing : 'easeOutStrong',
12885         widthIncrement : 0,
12886         heightIncrement : 0,
12887         pinned : false,
12888         width : null,
12889         height : null,
12890         preserveRatio : false,
12891         transparent: false,
12892         minX: 0,
12893         minY: 0,
12894         draggable: false,
12895
12896         /**
12897          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
12898          */
12899         constrainTo: undefined,
12900         /**
12901          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
12902          */
12903         resizeRegion: undefined,
12904
12905
12906     /**
12907      * Perform a manual resize
12908      * @param {Number} width
12909      * @param {Number} height
12910      */
12911     resizeTo : function(width, height){
12912         this.el.setSize(width, height);
12913         this.updateChildSize();
12914         this.fireEvent("resize", this, width, height, null);
12915     },
12916
12917     // private
12918     startSizing : function(e, handle){
12919         this.fireEvent("beforeresize", this, e);
12920         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
12921
12922             if(!this.overlay){
12923                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
12924                 this.overlay.unselectable();
12925                 this.overlay.enableDisplayMode("block");
12926                 this.overlay.on("mousemove", this.onMouseMove, this);
12927                 this.overlay.on("mouseup", this.onMouseUp, this);
12928             }
12929             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
12930
12931             this.resizing = true;
12932             this.startBox = this.el.getBox();
12933             this.startPoint = e.getXY();
12934             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
12935                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
12936
12937             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
12938             this.overlay.show();
12939
12940             if(this.constrainTo) {
12941                 var ct = Roo.get(this.constrainTo);
12942                 this.resizeRegion = ct.getRegion().adjust(
12943                     ct.getFrameWidth('t'),
12944                     ct.getFrameWidth('l'),
12945                     -ct.getFrameWidth('b'),
12946                     -ct.getFrameWidth('r')
12947                 );
12948             }
12949
12950             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
12951             this.proxy.show();
12952             this.proxy.setBox(this.startBox);
12953             if(!this.dynamic){
12954                 this.proxy.setStyle('visibility', 'visible');
12955             }
12956         }
12957     },
12958
12959     // private
12960     onMouseDown : function(handle, e){
12961         if(this.enabled){
12962             e.stopEvent();
12963             this.activeHandle = handle;
12964             this.startSizing(e, handle);
12965         }
12966     },
12967
12968     // private
12969     onMouseUp : function(e){
12970         var size = this.resizeElement();
12971         this.resizing = false;
12972         this.handleOut();
12973         this.overlay.hide();
12974         this.proxy.hide();
12975         this.fireEvent("resize", this, size.width, size.height, e);
12976     },
12977
12978     // private
12979     updateChildSize : function(){
12980         
12981         if(this.resizeChild){
12982             var el = this.el;
12983             var child = this.resizeChild;
12984             var adj = this.adjustments;
12985             if(el.dom.offsetWidth){
12986                 var b = el.getSize(true);
12987                 child.setSize(b.width+adj[0], b.height+adj[1]);
12988             }
12989             // Second call here for IE
12990             // The first call enables instant resizing and
12991             // the second call corrects scroll bars if they
12992             // exist
12993             if(Roo.isIE){
12994                 setTimeout(function(){
12995                     if(el.dom.offsetWidth){
12996                         var b = el.getSize(true);
12997                         child.setSize(b.width+adj[0], b.height+adj[1]);
12998                     }
12999                 }, 10);
13000             }
13001         }
13002     },
13003
13004     // private
13005     snap : function(value, inc, min){
13006         if(!inc || !value) return value;
13007         var newValue = value;
13008         var m = value % inc;
13009         if(m > 0){
13010             if(m > (inc/2)){
13011                 newValue = value + (inc-m);
13012             }else{
13013                 newValue = value - m;
13014             }
13015         }
13016         return Math.max(min, newValue);
13017     },
13018
13019     // private
13020     resizeElement : function(){
13021         var box = this.proxy.getBox();
13022         if(this.updateBox){
13023             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13024         }else{
13025             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13026         }
13027         this.updateChildSize();
13028         if(!this.dynamic){
13029             this.proxy.hide();
13030         }
13031         return box;
13032     },
13033
13034     // private
13035     constrain : function(v, diff, m, mx){
13036         if(v - diff < m){
13037             diff = v - m;
13038         }else if(v - diff > mx){
13039             diff = mx - v;
13040         }
13041         return diff;
13042     },
13043
13044     // private
13045     onMouseMove : function(e){
13046         
13047         if(this.enabled){
13048             try{// try catch so if something goes wrong the user doesn't get hung
13049
13050             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13051                 return;
13052             }
13053
13054             //var curXY = this.startPoint;
13055             var curSize = this.curSize || this.startBox;
13056             var x = this.startBox.x, y = this.startBox.y;
13057             var ox = x, oy = y;
13058             var w = curSize.width, h = curSize.height;
13059             var ow = w, oh = h;
13060             var mw = this.minWidth, mh = this.minHeight;
13061             var mxw = this.maxWidth, mxh = this.maxHeight;
13062             var wi = this.widthIncrement;
13063             var hi = this.heightIncrement;
13064
13065             var eventXY = e.getXY();
13066             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13067             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13068
13069             var pos = this.activeHandle.position;
13070
13071             switch(pos){
13072                 case "east":
13073                     w += diffX;
13074                     w = Math.min(Math.max(mw, w), mxw);
13075                     break;
13076              
13077                 case "south":
13078                     h += diffY;
13079                     h = Math.min(Math.max(mh, h), mxh);
13080                     break;
13081                 case "southeast":
13082                     w += diffX;
13083                     h += diffY;
13084                     w = Math.min(Math.max(mw, w), mxw);
13085                     h = Math.min(Math.max(mh, h), mxh);
13086                     break;
13087                 case "north":
13088                     diffY = this.constrain(h, diffY, mh, mxh);
13089                     y += diffY;
13090                     h -= diffY;
13091                     break;
13092                 case "hdrag":
13093                     
13094                     if (wi) {
13095                         var adiffX = Math.abs(diffX);
13096                         var sub = (adiffX % wi); // how much 
13097                         if (sub > (wi/2)) { // far enough to snap
13098                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13099                         } else {
13100                             // remove difference.. 
13101                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13102                         }
13103                     }
13104                     x += diffX;
13105                     x = Math.max(this.minX, x);
13106                     break;
13107                 case "west":
13108                     diffX = this.constrain(w, diffX, mw, mxw);
13109                     x += diffX;
13110                     w -= diffX;
13111                     break;
13112                 case "northeast":
13113                     w += diffX;
13114                     w = Math.min(Math.max(mw, w), mxw);
13115                     diffY = this.constrain(h, diffY, mh, mxh);
13116                     y += diffY;
13117                     h -= diffY;
13118                     break;
13119                 case "northwest":
13120                     diffX = this.constrain(w, diffX, mw, mxw);
13121                     diffY = this.constrain(h, diffY, mh, mxh);
13122                     y += diffY;
13123                     h -= diffY;
13124                     x += diffX;
13125                     w -= diffX;
13126                     break;
13127                case "southwest":
13128                     diffX = this.constrain(w, diffX, mw, mxw);
13129                     h += diffY;
13130                     h = Math.min(Math.max(mh, h), mxh);
13131                     x += diffX;
13132                     w -= diffX;
13133                     break;
13134             }
13135
13136             var sw = this.snap(w, wi, mw);
13137             var sh = this.snap(h, hi, mh);
13138             if(sw != w || sh != h){
13139                 switch(pos){
13140                     case "northeast":
13141                         y -= sh - h;
13142                     break;
13143                     case "north":
13144                         y -= sh - h;
13145                         break;
13146                     case "southwest":
13147                         x -= sw - w;
13148                     break;
13149                     case "west":
13150                         x -= sw - w;
13151                         break;
13152                     case "northwest":
13153                         x -= sw - w;
13154                         y -= sh - h;
13155                     break;
13156                 }
13157                 w = sw;
13158                 h = sh;
13159             }
13160
13161             if(this.preserveRatio){
13162                 switch(pos){
13163                     case "southeast":
13164                     case "east":
13165                         h = oh * (w/ow);
13166                         h = Math.min(Math.max(mh, h), mxh);
13167                         w = ow * (h/oh);
13168                        break;
13169                     case "south":
13170                         w = ow * (h/oh);
13171                         w = Math.min(Math.max(mw, w), mxw);
13172                         h = oh * (w/ow);
13173                         break;
13174                     case "northeast":
13175                         w = ow * (h/oh);
13176                         w = Math.min(Math.max(mw, w), mxw);
13177                         h = oh * (w/ow);
13178                     break;
13179                     case "north":
13180                         var tw = w;
13181                         w = ow * (h/oh);
13182                         w = Math.min(Math.max(mw, w), mxw);
13183                         h = oh * (w/ow);
13184                         x += (tw - w) / 2;
13185                         break;
13186                     case "southwest":
13187                         h = oh * (w/ow);
13188                         h = Math.min(Math.max(mh, h), mxh);
13189                         var tw = w;
13190                         w = ow * (h/oh);
13191                         x += tw - w;
13192                         break;
13193                     case "west":
13194                         var th = h;
13195                         h = oh * (w/ow);
13196                         h = Math.min(Math.max(mh, h), mxh);
13197                         y += (th - h) / 2;
13198                         var tw = w;
13199                         w = ow * (h/oh);
13200                         x += tw - w;
13201                        break;
13202                     case "northwest":
13203                         var tw = w;
13204                         var th = h;
13205                         h = oh * (w/ow);
13206                         h = Math.min(Math.max(mh, h), mxh);
13207                         w = ow * (h/oh);
13208                         y += th - h;
13209                         x += tw - w;
13210                        break;
13211
13212                 }
13213             }
13214             if (pos == 'hdrag') {
13215                 w = ow;
13216             }
13217             this.proxy.setBounds(x, y, w, h);
13218             if(this.dynamic){
13219                 this.resizeElement();
13220             }
13221             }catch(e){}
13222         }
13223         this.fireEvent("resizing", this, x, y, w, h, e);
13224     },
13225
13226     // private
13227     handleOver : function(){
13228         if(this.enabled){
13229             this.el.addClass("x-resizable-over");
13230         }
13231     },
13232
13233     // private
13234     handleOut : function(){
13235         if(!this.resizing){
13236             this.el.removeClass("x-resizable-over");
13237         }
13238     },
13239
13240     /**
13241      * Returns the element this component is bound to.
13242      * @return {Roo.Element}
13243      */
13244     getEl : function(){
13245         return this.el;
13246     },
13247
13248     /**
13249      * Returns the resizeChild element (or null).
13250      * @return {Roo.Element}
13251      */
13252     getResizeChild : function(){
13253         return this.resizeChild;
13254     },
13255     groupHandler : function()
13256     {
13257         
13258     },
13259     /**
13260      * Destroys this resizable. If the element was wrapped and
13261      * removeEl is not true then the element remains.
13262      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13263      */
13264     destroy : function(removeEl){
13265         this.proxy.remove();
13266         if(this.overlay){
13267             this.overlay.removeAllListeners();
13268             this.overlay.remove();
13269         }
13270         var ps = Roo.Resizable.positions;
13271         for(var k in ps){
13272             if(typeof ps[k] != "function" && this[ps[k]]){
13273                 var h = this[ps[k]];
13274                 h.el.removeAllListeners();
13275                 h.el.remove();
13276             }
13277         }
13278         if(removeEl){
13279             this.el.update("");
13280             this.el.remove();
13281         }
13282     }
13283 });
13284
13285 // private
13286 // hash to map config positions to true positions
13287 Roo.Resizable.positions = {
13288     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13289     hd: "hdrag"
13290 };
13291
13292 // private
13293 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13294     if(!this.tpl){
13295         // only initialize the template if resizable is used
13296         var tpl = Roo.DomHelper.createTemplate(
13297             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13298         );
13299         tpl.compile();
13300         Roo.Resizable.Handle.prototype.tpl = tpl;
13301     }
13302     this.position = pos;
13303     this.rz = rz;
13304     // show north drag fro topdra
13305     var handlepos = pos == 'hdrag' ? 'north' : pos;
13306     
13307     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13308     if (pos == 'hdrag') {
13309         this.el.setStyle('cursor', 'pointer');
13310     }
13311     this.el.unselectable();
13312     if(transparent){
13313         this.el.setOpacity(0);
13314     }
13315     this.el.on("mousedown", this.onMouseDown, this);
13316     if(!disableTrackOver){
13317         this.el.on("mouseover", this.onMouseOver, this);
13318         this.el.on("mouseout", this.onMouseOut, this);
13319     }
13320 };
13321
13322 // private
13323 Roo.Resizable.Handle.prototype = {
13324     afterResize : function(rz){
13325         Roo.log('after?');
13326         // do nothing
13327     },
13328     // private
13329     onMouseDown : function(e){
13330         this.rz.onMouseDown(this, e);
13331     },
13332     // private
13333     onMouseOver : function(e){
13334         this.rz.handleOver(this, e);
13335     },
13336     // private
13337     onMouseOut : function(e){
13338         this.rz.handleOut(this, e);
13339     }
13340 };/*
13341  * Based on:
13342  * Ext JS Library 1.1.1
13343  * Copyright(c) 2006-2007, Ext JS, LLC.
13344  *
13345  * Originally Released Under LGPL - original licence link has changed is not relivant.
13346  *
13347  * Fork - LGPL
13348  * <script type="text/javascript">
13349  */
13350
13351 /**
13352  * @class Roo.Editor
13353  * @extends Roo.Component
13354  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13355  * @constructor
13356  * Create a new Editor
13357  * @param {Roo.form.Field} field The Field object (or descendant)
13358  * @param {Object} config The config object
13359  */
13360 Roo.Editor = function(field, config){
13361     Roo.Editor.superclass.constructor.call(this, config);
13362     this.field = field;
13363     this.addEvents({
13364         /**
13365              * @event beforestartedit
13366              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13367              * false from the handler of this event.
13368              * @param {Editor} this
13369              * @param {Roo.Element} boundEl The underlying element bound to this editor
13370              * @param {Mixed} value The field value being set
13371              */
13372         "beforestartedit" : true,
13373         /**
13374              * @event startedit
13375              * Fires when this editor is displayed
13376              * @param {Roo.Element} boundEl The underlying element bound to this editor
13377              * @param {Mixed} value The starting field value
13378              */
13379         "startedit" : true,
13380         /**
13381              * @event beforecomplete
13382              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13383              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13384              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13385              * event will not fire since no edit actually occurred.
13386              * @param {Editor} this
13387              * @param {Mixed} value The current field value
13388              * @param {Mixed} startValue The original field value
13389              */
13390         "beforecomplete" : true,
13391         /**
13392              * @event complete
13393              * Fires after editing is complete and any changed value has been written to the underlying field.
13394              * @param {Editor} this
13395              * @param {Mixed} value The current field value
13396              * @param {Mixed} startValue The original field value
13397              */
13398         "complete" : true,
13399         /**
13400          * @event specialkey
13401          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13402          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13403          * @param {Roo.form.Field} this
13404          * @param {Roo.EventObject} e The event object
13405          */
13406         "specialkey" : true
13407     });
13408 };
13409
13410 Roo.extend(Roo.Editor, Roo.Component, {
13411     /**
13412      * @cfg {Boolean/String} autosize
13413      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13414      * or "height" to adopt the height only (defaults to false)
13415      */
13416     /**
13417      * @cfg {Boolean} revertInvalid
13418      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13419      * validation fails (defaults to true)
13420      */
13421     /**
13422      * @cfg {Boolean} ignoreNoChange
13423      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13424      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13425      * will never be ignored.
13426      */
13427     /**
13428      * @cfg {Boolean} hideEl
13429      * False to keep the bound element visible while the editor is displayed (defaults to true)
13430      */
13431     /**
13432      * @cfg {Mixed} value
13433      * The data value of the underlying field (defaults to "")
13434      */
13435     value : "",
13436     /**
13437      * @cfg {String} alignment
13438      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13439      */
13440     alignment: "c-c?",
13441     /**
13442      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13443      * for bottom-right shadow (defaults to "frame")
13444      */
13445     shadow : "frame",
13446     /**
13447      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13448      */
13449     constrain : false,
13450     /**
13451      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13452      */
13453     completeOnEnter : false,
13454     /**
13455      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13456      */
13457     cancelOnEsc : false,
13458     /**
13459      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13460      */
13461     updateEl : false,
13462
13463     // private
13464     onRender : function(ct, position){
13465         this.el = new Roo.Layer({
13466             shadow: this.shadow,
13467             cls: "x-editor",
13468             parentEl : ct,
13469             shim : this.shim,
13470             shadowOffset:4,
13471             id: this.id,
13472             constrain: this.constrain
13473         });
13474         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13475         if(this.field.msgTarget != 'title'){
13476             this.field.msgTarget = 'qtip';
13477         }
13478         this.field.render(this.el);
13479         if(Roo.isGecko){
13480             this.field.el.dom.setAttribute('autocomplete', 'off');
13481         }
13482         this.field.on("specialkey", this.onSpecialKey, this);
13483         if(this.swallowKeys){
13484             this.field.el.swallowEvent(['keydown','keypress']);
13485         }
13486         this.field.show();
13487         this.field.on("blur", this.onBlur, this);
13488         if(this.field.grow){
13489             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13490         }
13491     },
13492
13493     onSpecialKey : function(field, e)
13494     {
13495         //Roo.log('editor onSpecialKey');
13496         if(this.completeOnEnter && e.getKey() == e.ENTER){
13497             e.stopEvent();
13498             this.completeEdit();
13499             return;
13500         }
13501         // do not fire special key otherwise it might hide close the editor...
13502         if(e.getKey() == e.ENTER){    
13503             return;
13504         }
13505         if(this.cancelOnEsc && e.getKey() == e.ESC){
13506             this.cancelEdit();
13507             return;
13508         } 
13509         this.fireEvent('specialkey', field, e);
13510     
13511     },
13512
13513     /**
13514      * Starts the editing process and shows the editor.
13515      * @param {String/HTMLElement/Element} el The element to edit
13516      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13517       * to the innerHTML of el.
13518      */
13519     startEdit : function(el, value){
13520         if(this.editing){
13521             this.completeEdit();
13522         }
13523         this.boundEl = Roo.get(el);
13524         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13525         if(!this.rendered){
13526             this.render(this.parentEl || document.body);
13527         }
13528         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13529             return;
13530         }
13531         this.startValue = v;
13532         this.field.setValue(v);
13533         if(this.autoSize){
13534             var sz = this.boundEl.getSize();
13535             switch(this.autoSize){
13536                 case "width":
13537                 this.setSize(sz.width,  "");
13538                 break;
13539                 case "height":
13540                 this.setSize("",  sz.height);
13541                 break;
13542                 default:
13543                 this.setSize(sz.width,  sz.height);
13544             }
13545         }
13546         this.el.alignTo(this.boundEl, this.alignment);
13547         this.editing = true;
13548         if(Roo.QuickTips){
13549             Roo.QuickTips.disable();
13550         }
13551         this.show();
13552     },
13553
13554     /**
13555      * Sets the height and width of this editor.
13556      * @param {Number} width The new width
13557      * @param {Number} height The new height
13558      */
13559     setSize : function(w, h){
13560         this.field.setSize(w, h);
13561         if(this.el){
13562             this.el.sync();
13563         }
13564     },
13565
13566     /**
13567      * Realigns the editor to the bound field based on the current alignment config value.
13568      */
13569     realign : function(){
13570         this.el.alignTo(this.boundEl, this.alignment);
13571     },
13572
13573     /**
13574      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
13575      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
13576      */
13577     completeEdit : function(remainVisible){
13578         if(!this.editing){
13579             return;
13580         }
13581         var v = this.getValue();
13582         if(this.revertInvalid !== false && !this.field.isValid()){
13583             v = this.startValue;
13584             this.cancelEdit(true);
13585         }
13586         if(String(v) === String(this.startValue) && this.ignoreNoChange){
13587             this.editing = false;
13588             this.hide();
13589             return;
13590         }
13591         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
13592             this.editing = false;
13593             if(this.updateEl && this.boundEl){
13594                 this.boundEl.update(v);
13595             }
13596             if(remainVisible !== true){
13597                 this.hide();
13598             }
13599             this.fireEvent("complete", this, v, this.startValue);
13600         }
13601     },
13602
13603     // private
13604     onShow : function(){
13605         this.el.show();
13606         if(this.hideEl !== false){
13607             this.boundEl.hide();
13608         }
13609         this.field.show();
13610         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
13611             this.fixIEFocus = true;
13612             this.deferredFocus.defer(50, this);
13613         }else{
13614             this.field.focus();
13615         }
13616         this.fireEvent("startedit", this.boundEl, this.startValue);
13617     },
13618
13619     deferredFocus : function(){
13620         if(this.editing){
13621             this.field.focus();
13622         }
13623     },
13624
13625     /**
13626      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
13627      * reverted to the original starting value.
13628      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
13629      * cancel (defaults to false)
13630      */
13631     cancelEdit : function(remainVisible){
13632         if(this.editing){
13633             this.setValue(this.startValue);
13634             if(remainVisible !== true){
13635                 this.hide();
13636             }
13637         }
13638     },
13639
13640     // private
13641     onBlur : function(){
13642         if(this.allowBlur !== true && this.editing){
13643             this.completeEdit();
13644         }
13645     },
13646
13647     // private
13648     onHide : function(){
13649         if(this.editing){
13650             this.completeEdit();
13651             return;
13652         }
13653         this.field.blur();
13654         if(this.field.collapse){
13655             this.field.collapse();
13656         }
13657         this.el.hide();
13658         if(this.hideEl !== false){
13659             this.boundEl.show();
13660         }
13661         if(Roo.QuickTips){
13662             Roo.QuickTips.enable();
13663         }
13664     },
13665
13666     /**
13667      * Sets the data value of the editor
13668      * @param {Mixed} value Any valid value supported by the underlying field
13669      */
13670     setValue : function(v){
13671         this.field.setValue(v);
13672     },
13673
13674     /**
13675      * Gets the data value of the editor
13676      * @return {Mixed} The data value
13677      */
13678     getValue : function(){
13679         return this.field.getValue();
13680     }
13681 });/*
13682  * Based on:
13683  * Ext JS Library 1.1.1
13684  * Copyright(c) 2006-2007, Ext JS, LLC.
13685  *
13686  * Originally Released Under LGPL - original licence link has changed is not relivant.
13687  *
13688  * Fork - LGPL
13689  * <script type="text/javascript">
13690  */
13691  
13692 /**
13693  * @class Roo.BasicDialog
13694  * @extends Roo.util.Observable
13695  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
13696  * <pre><code>
13697 var dlg = new Roo.BasicDialog("my-dlg", {
13698     height: 200,
13699     width: 300,
13700     minHeight: 100,
13701     minWidth: 150,
13702     modal: true,
13703     proxyDrag: true,
13704     shadow: true
13705 });
13706 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
13707 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
13708 dlg.addButton('Cancel', dlg.hide, dlg);
13709 dlg.show();
13710 </code></pre>
13711   <b>A Dialog should always be a direct child of the body element.</b>
13712  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
13713  * @cfg {String} title Default text to display in the title bar (defaults to null)
13714  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
13715  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
13716  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
13717  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
13718  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
13719  * (defaults to null with no animation)
13720  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
13721  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
13722  * property for valid values (defaults to 'all')
13723  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
13724  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
13725  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
13726  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
13727  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
13728  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
13729  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
13730  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
13731  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
13732  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
13733  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
13734  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
13735  * draggable = true (defaults to false)
13736  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
13737  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13738  * shadow (defaults to false)
13739  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
13740  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
13741  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
13742  * @cfg {Array} buttons Array of buttons
13743  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
13744  * @constructor
13745  * Create a new BasicDialog.
13746  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
13747  * @param {Object} config Configuration options
13748  */
13749 Roo.BasicDialog = function(el, config){
13750     this.el = Roo.get(el);
13751     var dh = Roo.DomHelper;
13752     if(!this.el && config && config.autoCreate){
13753         if(typeof config.autoCreate == "object"){
13754             if(!config.autoCreate.id){
13755                 config.autoCreate.id = el;
13756             }
13757             this.el = dh.append(document.body,
13758                         config.autoCreate, true);
13759         }else{
13760             this.el = dh.append(document.body,
13761                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
13762         }
13763     }
13764     el = this.el;
13765     el.setDisplayed(true);
13766     el.hide = this.hideAction;
13767     this.id = el.id;
13768     el.addClass("x-dlg");
13769
13770     Roo.apply(this, config);
13771
13772     this.proxy = el.createProxy("x-dlg-proxy");
13773     this.proxy.hide = this.hideAction;
13774     this.proxy.setOpacity(.5);
13775     this.proxy.hide();
13776
13777     if(config.width){
13778         el.setWidth(config.width);
13779     }
13780     if(config.height){
13781         el.setHeight(config.height);
13782     }
13783     this.size = el.getSize();
13784     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
13785         this.xy = [config.x,config.y];
13786     }else{
13787         this.xy = el.getCenterXY(true);
13788     }
13789     /** The header element @type Roo.Element */
13790     this.header = el.child("> .x-dlg-hd");
13791     /** The body element @type Roo.Element */
13792     this.body = el.child("> .x-dlg-bd");
13793     /** The footer element @type Roo.Element */
13794     this.footer = el.child("> .x-dlg-ft");
13795
13796     if(!this.header){
13797         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
13798     }
13799     if(!this.body){
13800         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
13801     }
13802
13803     this.header.unselectable();
13804     if(this.title){
13805         this.header.update(this.title);
13806     }
13807     // this element allows the dialog to be focused for keyboard event
13808     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
13809     this.focusEl.swallowEvent("click", true);
13810
13811     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
13812
13813     // wrap the body and footer for special rendering
13814     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
13815     if(this.footer){
13816         this.bwrap.dom.appendChild(this.footer.dom);
13817     }
13818
13819     this.bg = this.el.createChild({
13820         tag: "div", cls:"x-dlg-bg",
13821         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
13822     });
13823     this.centerBg = this.bg.child("div.x-dlg-bg-center");
13824
13825
13826     if(this.autoScroll !== false && !this.autoTabs){
13827         this.body.setStyle("overflow", "auto");
13828     }
13829
13830     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
13831
13832     if(this.closable !== false){
13833         this.el.addClass("x-dlg-closable");
13834         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
13835         this.close.on("click", this.closeClick, this);
13836         this.close.addClassOnOver("x-dlg-close-over");
13837     }
13838     if(this.collapsible !== false){
13839         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
13840         this.collapseBtn.on("click", this.collapseClick, this);
13841         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
13842         this.header.on("dblclick", this.collapseClick, this);
13843     }
13844     if(this.resizable !== false){
13845         this.el.addClass("x-dlg-resizable");
13846         this.resizer = new Roo.Resizable(el, {
13847             minWidth: this.minWidth || 80,
13848             minHeight:this.minHeight || 80,
13849             handles: this.resizeHandles || "all",
13850             pinned: true
13851         });
13852         this.resizer.on("beforeresize", this.beforeResize, this);
13853         this.resizer.on("resize", this.onResize, this);
13854     }
13855     if(this.draggable !== false){
13856         el.addClass("x-dlg-draggable");
13857         if (!this.proxyDrag) {
13858             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
13859         }
13860         else {
13861             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
13862         }
13863         dd.setHandleElId(this.header.id);
13864         dd.endDrag = this.endMove.createDelegate(this);
13865         dd.startDrag = this.startMove.createDelegate(this);
13866         dd.onDrag = this.onDrag.createDelegate(this);
13867         dd.scroll = false;
13868         this.dd = dd;
13869     }
13870     if(this.modal){
13871         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
13872         this.mask.enableDisplayMode("block");
13873         this.mask.hide();
13874         this.el.addClass("x-dlg-modal");
13875     }
13876     if(this.shadow){
13877         this.shadow = new Roo.Shadow({
13878             mode : typeof this.shadow == "string" ? this.shadow : "sides",
13879             offset : this.shadowOffset
13880         });
13881     }else{
13882         this.shadowOffset = 0;
13883     }
13884     if(Roo.useShims && this.shim !== false){
13885         this.shim = this.el.createShim();
13886         this.shim.hide = this.hideAction;
13887         this.shim.hide();
13888     }else{
13889         this.shim = false;
13890     }
13891     if(this.autoTabs){
13892         this.initTabs();
13893     }
13894     if (this.buttons) { 
13895         var bts= this.buttons;
13896         this.buttons = [];
13897         Roo.each(bts, function(b) {
13898             this.addButton(b);
13899         }, this);
13900     }
13901     
13902     
13903     this.addEvents({
13904         /**
13905          * @event keydown
13906          * Fires when a key is pressed
13907          * @param {Roo.BasicDialog} this
13908          * @param {Roo.EventObject} e
13909          */
13910         "keydown" : true,
13911         /**
13912          * @event move
13913          * Fires when this dialog is moved by the user.
13914          * @param {Roo.BasicDialog} this
13915          * @param {Number} x The new page X
13916          * @param {Number} y The new page Y
13917          */
13918         "move" : true,
13919         /**
13920          * @event resize
13921          * Fires when this dialog is resized by the user.
13922          * @param {Roo.BasicDialog} this
13923          * @param {Number} width The new width
13924          * @param {Number} height The new height
13925          */
13926         "resize" : true,
13927         /**
13928          * @event beforehide
13929          * Fires before this dialog is hidden.
13930          * @param {Roo.BasicDialog} this
13931          */
13932         "beforehide" : true,
13933         /**
13934          * @event hide
13935          * Fires when this dialog is hidden.
13936          * @param {Roo.BasicDialog} this
13937          */
13938         "hide" : true,
13939         /**
13940          * @event beforeshow
13941          * Fires before this dialog is shown.
13942          * @param {Roo.BasicDialog} this
13943          */
13944         "beforeshow" : true,
13945         /**
13946          * @event show
13947          * Fires when this dialog is shown.
13948          * @param {Roo.BasicDialog} this
13949          */
13950         "show" : true
13951     });
13952     el.on("keydown", this.onKeyDown, this);
13953     el.on("mousedown", this.toFront, this);
13954     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
13955     this.el.hide();
13956     Roo.DialogManager.register(this);
13957     Roo.BasicDialog.superclass.constructor.call(this);
13958 };
13959
13960 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
13961     shadowOffset: Roo.isIE ? 6 : 5,
13962     minHeight: 80,
13963     minWidth: 200,
13964     minButtonWidth: 75,
13965     defaultButton: null,
13966     buttonAlign: "right",
13967     tabTag: 'div',
13968     firstShow: true,
13969
13970     /**
13971      * Sets the dialog title text
13972      * @param {String} text The title text to display
13973      * @return {Roo.BasicDialog} this
13974      */
13975     setTitle : function(text){
13976         this.header.update(text);
13977         return this;
13978     },
13979
13980     // private
13981     closeClick : function(){
13982         this.hide();
13983     },
13984
13985     // private
13986     collapseClick : function(){
13987         this[this.collapsed ? "expand" : "collapse"]();
13988     },
13989
13990     /**
13991      * Collapses the dialog to its minimized state (only the title bar is visible).
13992      * Equivalent to the user clicking the collapse dialog button.
13993      */
13994     collapse : function(){
13995         if(!this.collapsed){
13996             this.collapsed = true;
13997             this.el.addClass("x-dlg-collapsed");
13998             this.restoreHeight = this.el.getHeight();
13999             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14000         }
14001     },
14002
14003     /**
14004      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14005      * clicking the expand dialog button.
14006      */
14007     expand : function(){
14008         if(this.collapsed){
14009             this.collapsed = false;
14010             this.el.removeClass("x-dlg-collapsed");
14011             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14012         }
14013     },
14014
14015     /**
14016      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14017      * @return {Roo.TabPanel} The tabs component
14018      */
14019     initTabs : function(){
14020         var tabs = this.getTabs();
14021         while(tabs.getTab(0)){
14022             tabs.removeTab(0);
14023         }
14024         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14025             var dom = el.dom;
14026             tabs.addTab(Roo.id(dom), dom.title);
14027             dom.title = "";
14028         });
14029         tabs.activate(0);
14030         return tabs;
14031     },
14032
14033     // private
14034     beforeResize : function(){
14035         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14036     },
14037
14038     // private
14039     onResize : function(){
14040         this.refreshSize();
14041         this.syncBodyHeight();
14042         this.adjustAssets();
14043         this.focus();
14044         this.fireEvent("resize", this, this.size.width, this.size.height);
14045     },
14046
14047     // private
14048     onKeyDown : function(e){
14049         if(this.isVisible()){
14050             this.fireEvent("keydown", this, e);
14051         }
14052     },
14053
14054     /**
14055      * Resizes the dialog.
14056      * @param {Number} width
14057      * @param {Number} height
14058      * @return {Roo.BasicDialog} this
14059      */
14060     resizeTo : function(width, height){
14061         this.el.setSize(width, height);
14062         this.size = {width: width, height: height};
14063         this.syncBodyHeight();
14064         if(this.fixedcenter){
14065             this.center();
14066         }
14067         if(this.isVisible()){
14068             this.constrainXY();
14069             this.adjustAssets();
14070         }
14071         this.fireEvent("resize", this, width, height);
14072         return this;
14073     },
14074
14075
14076     /**
14077      * Resizes the dialog to fit the specified content size.
14078      * @param {Number} width
14079      * @param {Number} height
14080      * @return {Roo.BasicDialog} this
14081      */
14082     setContentSize : function(w, h){
14083         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14084         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14085         //if(!this.el.isBorderBox()){
14086             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14087             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14088         //}
14089         if(this.tabs){
14090             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14091             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14092         }
14093         this.resizeTo(w, h);
14094         return this;
14095     },
14096
14097     /**
14098      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14099      * executed in response to a particular key being pressed while the dialog is active.
14100      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14101      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14102      * @param {Function} fn The function to call
14103      * @param {Object} scope (optional) The scope of the function
14104      * @return {Roo.BasicDialog} this
14105      */
14106     addKeyListener : function(key, fn, scope){
14107         var keyCode, shift, ctrl, alt;
14108         if(typeof key == "object" && !(key instanceof Array)){
14109             keyCode = key["key"];
14110             shift = key["shift"];
14111             ctrl = key["ctrl"];
14112             alt = key["alt"];
14113         }else{
14114             keyCode = key;
14115         }
14116         var handler = function(dlg, e){
14117             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14118                 var k = e.getKey();
14119                 if(keyCode instanceof Array){
14120                     for(var i = 0, len = keyCode.length; i < len; i++){
14121                         if(keyCode[i] == k){
14122                           fn.call(scope || window, dlg, k, e);
14123                           return;
14124                         }
14125                     }
14126                 }else{
14127                     if(k == keyCode){
14128                         fn.call(scope || window, dlg, k, e);
14129                     }
14130                 }
14131             }
14132         };
14133         this.on("keydown", handler);
14134         return this;
14135     },
14136
14137     /**
14138      * Returns the TabPanel component (creates it if it doesn't exist).
14139      * Note: If you wish to simply check for the existence of tabs without creating them,
14140      * check for a null 'tabs' property.
14141      * @return {Roo.TabPanel} The tabs component
14142      */
14143     getTabs : function(){
14144         if(!this.tabs){
14145             this.el.addClass("x-dlg-auto-tabs");
14146             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14147             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14148         }
14149         return this.tabs;
14150     },
14151
14152     /**
14153      * Adds a button to the footer section of the dialog.
14154      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14155      * object or a valid Roo.DomHelper element config
14156      * @param {Function} handler The function called when the button is clicked
14157      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14158      * @return {Roo.Button} The new button
14159      */
14160     addButton : function(config, handler, scope){
14161         var dh = Roo.DomHelper;
14162         if(!this.footer){
14163             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14164         }
14165         if(!this.btnContainer){
14166             var tb = this.footer.createChild({
14167
14168                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14169                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14170             }, null, true);
14171             this.btnContainer = tb.firstChild.firstChild.firstChild;
14172         }
14173         var bconfig = {
14174             handler: handler,
14175             scope: scope,
14176             minWidth: this.minButtonWidth,
14177             hideParent:true
14178         };
14179         if(typeof config == "string"){
14180             bconfig.text = config;
14181         }else{
14182             if(config.tag){
14183                 bconfig.dhconfig = config;
14184             }else{
14185                 Roo.apply(bconfig, config);
14186             }
14187         }
14188         var fc = false;
14189         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14190             bconfig.position = Math.max(0, bconfig.position);
14191             fc = this.btnContainer.childNodes[bconfig.position];
14192         }
14193          
14194         var btn = new Roo.Button(
14195             fc ? 
14196                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14197                 : this.btnContainer.appendChild(document.createElement("td")),
14198             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14199             bconfig
14200         );
14201         this.syncBodyHeight();
14202         if(!this.buttons){
14203             /**
14204              * Array of all the buttons that have been added to this dialog via addButton
14205              * @type Array
14206              */
14207             this.buttons = [];
14208         }
14209         this.buttons.push(btn);
14210         return btn;
14211     },
14212
14213     /**
14214      * Sets the default button to be focused when the dialog is displayed.
14215      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14216      * @return {Roo.BasicDialog} this
14217      */
14218     setDefaultButton : function(btn){
14219         this.defaultButton = btn;
14220         return this;
14221     },
14222
14223     // private
14224     getHeaderFooterHeight : function(safe){
14225         var height = 0;
14226         if(this.header){
14227            height += this.header.getHeight();
14228         }
14229         if(this.footer){
14230            var fm = this.footer.getMargins();
14231             height += (this.footer.getHeight()+fm.top+fm.bottom);
14232         }
14233         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14234         height += this.centerBg.getPadding("tb");
14235         return height;
14236     },
14237
14238     // private
14239     syncBodyHeight : function()
14240     {
14241         var bd = this.body, // the text
14242             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
14243             bw = this.bwrap;
14244         var height = this.size.height - this.getHeaderFooterHeight(false);
14245         bd.setHeight(height-bd.getMargins("tb"));
14246         var hh = this.header.getHeight();
14247         var h = this.size.height-hh;
14248         cb.setHeight(h);
14249         
14250         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14251         bw.setHeight(h-cb.getPadding("tb"));
14252         
14253         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14254         bd.setWidth(bw.getWidth(true));
14255         if(this.tabs){
14256             this.tabs.syncHeight();
14257             if(Roo.isIE){
14258                 this.tabs.el.repaint();
14259             }
14260         }
14261     },
14262
14263     /**
14264      * Restores the previous state of the dialog if Roo.state is configured.
14265      * @return {Roo.BasicDialog} this
14266      */
14267     restoreState : function(){
14268         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14269         if(box && box.width){
14270             this.xy = [box.x, box.y];
14271             this.resizeTo(box.width, box.height);
14272         }
14273         return this;
14274     },
14275
14276     // private
14277     beforeShow : function(){
14278         this.expand();
14279         if(this.fixedcenter){
14280             this.xy = this.el.getCenterXY(true);
14281         }
14282         if(this.modal){
14283             Roo.get(document.body).addClass("x-body-masked");
14284             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14285             this.mask.show();
14286         }
14287         this.constrainXY();
14288     },
14289
14290     // private
14291     animShow : function(){
14292         var b = Roo.get(this.animateTarget).getBox();
14293         this.proxy.setSize(b.width, b.height);
14294         this.proxy.setLocation(b.x, b.y);
14295         this.proxy.show();
14296         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14297                     true, .35, this.showEl.createDelegate(this));
14298     },
14299
14300     /**
14301      * Shows the dialog.
14302      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14303      * @return {Roo.BasicDialog} this
14304      */
14305     show : function(animateTarget){
14306         if (this.fireEvent("beforeshow", this) === false){
14307             return;
14308         }
14309         if(this.syncHeightBeforeShow){
14310             this.syncBodyHeight();
14311         }else if(this.firstShow){
14312             this.firstShow = false;
14313             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14314         }
14315         this.animateTarget = animateTarget || this.animateTarget;
14316         if(!this.el.isVisible()){
14317             this.beforeShow();
14318             if(this.animateTarget && Roo.get(this.animateTarget)){
14319                 this.animShow();
14320             }else{
14321                 this.showEl();
14322             }
14323         }
14324         return this;
14325     },
14326
14327     // private
14328     showEl : function(){
14329         this.proxy.hide();
14330         this.el.setXY(this.xy);
14331         this.el.show();
14332         this.adjustAssets(true);
14333         this.toFront();
14334         this.focus();
14335         // IE peekaboo bug - fix found by Dave Fenwick
14336         if(Roo.isIE){
14337             this.el.repaint();
14338         }
14339         this.fireEvent("show", this);
14340     },
14341
14342     /**
14343      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14344      * dialog itself will receive focus.
14345      */
14346     focus : function(){
14347         if(this.defaultButton){
14348             this.defaultButton.focus();
14349         }else{
14350             this.focusEl.focus();
14351         }
14352     },
14353
14354     // private
14355     constrainXY : function(){
14356         if(this.constraintoviewport !== false){
14357             if(!this.viewSize){
14358                 if(this.container){
14359                     var s = this.container.getSize();
14360                     this.viewSize = [s.width, s.height];
14361                 }else{
14362                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14363                 }
14364             }
14365             var s = Roo.get(this.container||document).getScroll();
14366
14367             var x = this.xy[0], y = this.xy[1];
14368             var w = this.size.width, h = this.size.height;
14369             var vw = this.viewSize[0], vh = this.viewSize[1];
14370             // only move it if it needs it
14371             var moved = false;
14372             // first validate right/bottom
14373             if(x + w > vw+s.left){
14374                 x = vw - w;
14375                 moved = true;
14376             }
14377             if(y + h > vh+s.top){
14378                 y = vh - h;
14379                 moved = true;
14380             }
14381             // then make sure top/left isn't negative
14382             if(x < s.left){
14383                 x = s.left;
14384                 moved = true;
14385             }
14386             if(y < s.top){
14387                 y = s.top;
14388                 moved = true;
14389             }
14390             if(moved){
14391                 // cache xy
14392                 this.xy = [x, y];
14393                 if(this.isVisible()){
14394                     this.el.setLocation(x, y);
14395                     this.adjustAssets();
14396                 }
14397             }
14398         }
14399     },
14400
14401     // private
14402     onDrag : function(){
14403         if(!this.proxyDrag){
14404             this.xy = this.el.getXY();
14405             this.adjustAssets();
14406         }
14407     },
14408
14409     // private
14410     adjustAssets : function(doShow){
14411         var x = this.xy[0], y = this.xy[1];
14412         var w = this.size.width, h = this.size.height;
14413         if(doShow === true){
14414             if(this.shadow){
14415                 this.shadow.show(this.el);
14416             }
14417             if(this.shim){
14418                 this.shim.show();
14419             }
14420         }
14421         if(this.shadow && this.shadow.isVisible()){
14422             this.shadow.show(this.el);
14423         }
14424         if(this.shim && this.shim.isVisible()){
14425             this.shim.setBounds(x, y, w, h);
14426         }
14427     },
14428
14429     // private
14430     adjustViewport : function(w, h){
14431         if(!w || !h){
14432             w = Roo.lib.Dom.getViewWidth();
14433             h = Roo.lib.Dom.getViewHeight();
14434         }
14435         // cache the size
14436         this.viewSize = [w, h];
14437         if(this.modal && this.mask.isVisible()){
14438             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14439             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14440         }
14441         if(this.isVisible()){
14442             this.constrainXY();
14443         }
14444     },
14445
14446     /**
14447      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14448      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14449      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14450      */
14451     destroy : function(removeEl){
14452         if(this.isVisible()){
14453             this.animateTarget = null;
14454             this.hide();
14455         }
14456         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14457         if(this.tabs){
14458             this.tabs.destroy(removeEl);
14459         }
14460         Roo.destroy(
14461              this.shim,
14462              this.proxy,
14463              this.resizer,
14464              this.close,
14465              this.mask
14466         );
14467         if(this.dd){
14468             this.dd.unreg();
14469         }
14470         if(this.buttons){
14471            for(var i = 0, len = this.buttons.length; i < len; i++){
14472                this.buttons[i].destroy();
14473            }
14474         }
14475         this.el.removeAllListeners();
14476         if(removeEl === true){
14477             this.el.update("");
14478             this.el.remove();
14479         }
14480         Roo.DialogManager.unregister(this);
14481     },
14482
14483     // private
14484     startMove : function(){
14485         if(this.proxyDrag){
14486             this.proxy.show();
14487         }
14488         if(this.constraintoviewport !== false){
14489             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14490         }
14491     },
14492
14493     // private
14494     endMove : function(){
14495         if(!this.proxyDrag){
14496             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14497         }else{
14498             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14499             this.proxy.hide();
14500         }
14501         this.refreshSize();
14502         this.adjustAssets();
14503         this.focus();
14504         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14505     },
14506
14507     /**
14508      * Brings this dialog to the front of any other visible dialogs
14509      * @return {Roo.BasicDialog} this
14510      */
14511     toFront : function(){
14512         Roo.DialogManager.bringToFront(this);
14513         return this;
14514     },
14515
14516     /**
14517      * Sends this dialog to the back (under) of any other visible dialogs
14518      * @return {Roo.BasicDialog} this
14519      */
14520     toBack : function(){
14521         Roo.DialogManager.sendToBack(this);
14522         return this;
14523     },
14524
14525     /**
14526      * Centers this dialog in the viewport
14527      * @return {Roo.BasicDialog} this
14528      */
14529     center : function(){
14530         var xy = this.el.getCenterXY(true);
14531         this.moveTo(xy[0], xy[1]);
14532         return this;
14533     },
14534
14535     /**
14536      * Moves the dialog's top-left corner to the specified point
14537      * @param {Number} x
14538      * @param {Number} y
14539      * @return {Roo.BasicDialog} this
14540      */
14541     moveTo : function(x, y){
14542         this.xy = [x,y];
14543         if(this.isVisible()){
14544             this.el.setXY(this.xy);
14545             this.adjustAssets();
14546         }
14547         return this;
14548     },
14549
14550     /**
14551      * Aligns the dialog to the specified element
14552      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14553      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
14554      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14555      * @return {Roo.BasicDialog} this
14556      */
14557     alignTo : function(element, position, offsets){
14558         this.xy = this.el.getAlignToXY(element, position, offsets);
14559         if(this.isVisible()){
14560             this.el.setXY(this.xy);
14561             this.adjustAssets();
14562         }
14563         return this;
14564     },
14565
14566     /**
14567      * Anchors an element to another element and realigns it when the window is resized.
14568      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14569      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
14570      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14571      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
14572      * is a number, it is used as the buffer delay (defaults to 50ms).
14573      * @return {Roo.BasicDialog} this
14574      */
14575     anchorTo : function(el, alignment, offsets, monitorScroll){
14576         var action = function(){
14577             this.alignTo(el, alignment, offsets);
14578         };
14579         Roo.EventManager.onWindowResize(action, this);
14580         var tm = typeof monitorScroll;
14581         if(tm != 'undefined'){
14582             Roo.EventManager.on(window, 'scroll', action, this,
14583                 {buffer: tm == 'number' ? monitorScroll : 50});
14584         }
14585         action.call(this);
14586         return this;
14587     },
14588
14589     /**
14590      * Returns true if the dialog is visible
14591      * @return {Boolean}
14592      */
14593     isVisible : function(){
14594         return this.el.isVisible();
14595     },
14596
14597     // private
14598     animHide : function(callback){
14599         var b = Roo.get(this.animateTarget).getBox();
14600         this.proxy.show();
14601         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
14602         this.el.hide();
14603         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
14604                     this.hideEl.createDelegate(this, [callback]));
14605     },
14606
14607     /**
14608      * Hides the dialog.
14609      * @param {Function} callback (optional) Function to call when the dialog is hidden
14610      * @return {Roo.BasicDialog} this
14611      */
14612     hide : function(callback){
14613         if (this.fireEvent("beforehide", this) === false){
14614             return;
14615         }
14616         if(this.shadow){
14617             this.shadow.hide();
14618         }
14619         if(this.shim) {
14620           this.shim.hide();
14621         }
14622         // sometimes animateTarget seems to get set.. causing problems...
14623         // this just double checks..
14624         if(this.animateTarget && Roo.get(this.animateTarget)) {
14625            this.animHide(callback);
14626         }else{
14627             this.el.hide();
14628             this.hideEl(callback);
14629         }
14630         return this;
14631     },
14632
14633     // private
14634     hideEl : function(callback){
14635         this.proxy.hide();
14636         if(this.modal){
14637             this.mask.hide();
14638             Roo.get(document.body).removeClass("x-body-masked");
14639         }
14640         this.fireEvent("hide", this);
14641         if(typeof callback == "function"){
14642             callback();
14643         }
14644     },
14645
14646     // private
14647     hideAction : function(){
14648         this.setLeft("-10000px");
14649         this.setTop("-10000px");
14650         this.setStyle("visibility", "hidden");
14651     },
14652
14653     // private
14654     refreshSize : function(){
14655         this.size = this.el.getSize();
14656         this.xy = this.el.getXY();
14657         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
14658     },
14659
14660     // private
14661     // z-index is managed by the DialogManager and may be overwritten at any time
14662     setZIndex : function(index){
14663         if(this.modal){
14664             this.mask.setStyle("z-index", index);
14665         }
14666         if(this.shim){
14667             this.shim.setStyle("z-index", ++index);
14668         }
14669         if(this.shadow){
14670             this.shadow.setZIndex(++index);
14671         }
14672         this.el.setStyle("z-index", ++index);
14673         if(this.proxy){
14674             this.proxy.setStyle("z-index", ++index);
14675         }
14676         if(this.resizer){
14677             this.resizer.proxy.setStyle("z-index", ++index);
14678         }
14679
14680         this.lastZIndex = index;
14681     },
14682
14683     /**
14684      * Returns the element for this dialog
14685      * @return {Roo.Element} The underlying dialog Element
14686      */
14687     getEl : function(){
14688         return this.el;
14689     }
14690 });
14691
14692 /**
14693  * @class Roo.DialogManager
14694  * Provides global access to BasicDialogs that have been created and
14695  * support for z-indexing (layering) multiple open dialogs.
14696  */
14697 Roo.DialogManager = function(){
14698     var list = {};
14699     var accessList = [];
14700     var front = null;
14701
14702     // private
14703     var sortDialogs = function(d1, d2){
14704         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
14705     };
14706
14707     // private
14708     var orderDialogs = function(){
14709         accessList.sort(sortDialogs);
14710         var seed = Roo.DialogManager.zseed;
14711         for(var i = 0, len = accessList.length; i < len; i++){
14712             var dlg = accessList[i];
14713             if(dlg){
14714                 dlg.setZIndex(seed + (i*10));
14715             }
14716         }
14717     };
14718
14719     return {
14720         /**
14721          * The starting z-index for BasicDialogs (defaults to 9000)
14722          * @type Number The z-index value
14723          */
14724         zseed : 9000,
14725
14726         // private
14727         register : function(dlg){
14728             list[dlg.id] = dlg;
14729             accessList.push(dlg);
14730         },
14731
14732         // private
14733         unregister : function(dlg){
14734             delete list[dlg.id];
14735             var i=0;
14736             var len=0;
14737             if(!accessList.indexOf){
14738                 for(  i = 0, len = accessList.length; i < len; i++){
14739                     if(accessList[i] == dlg){
14740                         accessList.splice(i, 1);
14741                         return;
14742                     }
14743                 }
14744             }else{
14745                  i = accessList.indexOf(dlg);
14746                 if(i != -1){
14747                     accessList.splice(i, 1);
14748                 }
14749             }
14750         },
14751
14752         /**
14753          * Gets a registered dialog by id
14754          * @param {String/Object} id The id of the dialog or a dialog
14755          * @return {Roo.BasicDialog} this
14756          */
14757         get : function(id){
14758             return typeof id == "object" ? id : list[id];
14759         },
14760
14761         /**
14762          * Brings the specified dialog to the front
14763          * @param {String/Object} dlg The id of the dialog or a dialog
14764          * @return {Roo.BasicDialog} this
14765          */
14766         bringToFront : function(dlg){
14767             dlg = this.get(dlg);
14768             if(dlg != front){
14769                 front = dlg;
14770                 dlg._lastAccess = new Date().getTime();
14771                 orderDialogs();
14772             }
14773             return dlg;
14774         },
14775
14776         /**
14777          * Sends the specified dialog to the back
14778          * @param {String/Object} dlg The id of the dialog or a dialog
14779          * @return {Roo.BasicDialog} this
14780          */
14781         sendToBack : function(dlg){
14782             dlg = this.get(dlg);
14783             dlg._lastAccess = -(new Date().getTime());
14784             orderDialogs();
14785             return dlg;
14786         },
14787
14788         /**
14789          * Hides all dialogs
14790          */
14791         hideAll : function(){
14792             for(var id in list){
14793                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
14794                     list[id].hide();
14795                 }
14796             }
14797         }
14798     };
14799 }();
14800
14801 /**
14802  * @class Roo.LayoutDialog
14803  * @extends Roo.BasicDialog
14804  * Dialog which provides adjustments for working with a layout in a Dialog.
14805  * Add your necessary layout config options to the dialog's config.<br>
14806  * Example usage (including a nested layout):
14807  * <pre><code>
14808 if(!dialog){
14809     dialog = new Roo.LayoutDialog("download-dlg", {
14810         modal: true,
14811         width:600,
14812         height:450,
14813         shadow:true,
14814         minWidth:500,
14815         minHeight:350,
14816         autoTabs:true,
14817         proxyDrag:true,
14818         // layout config merges with the dialog config
14819         center:{
14820             tabPosition: "top",
14821             alwaysShowTabs: true
14822         }
14823     });
14824     dialog.addKeyListener(27, dialog.hide, dialog);
14825     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
14826     dialog.addButton("Build It!", this.getDownload, this);
14827
14828     // we can even add nested layouts
14829     var innerLayout = new Roo.BorderLayout("dl-inner", {
14830         east: {
14831             initialSize: 200,
14832             autoScroll:true,
14833             split:true
14834         },
14835         center: {
14836             autoScroll:true
14837         }
14838     });
14839     innerLayout.beginUpdate();
14840     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
14841     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
14842     innerLayout.endUpdate(true);
14843
14844     var layout = dialog.getLayout();
14845     layout.beginUpdate();
14846     layout.add("center", new Roo.ContentPanel("standard-panel",
14847                         {title: "Download the Source", fitToFrame:true}));
14848     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
14849                {title: "Build your own roo.js"}));
14850     layout.getRegion("center").showPanel(sp);
14851     layout.endUpdate();
14852 }
14853 </code></pre>
14854     * @constructor
14855     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
14856     * @param {Object} config configuration options
14857   */
14858 Roo.LayoutDialog = function(el, cfg){
14859     
14860     var config=  cfg;
14861     if (typeof(cfg) == 'undefined') {
14862         config = Roo.apply({}, el);
14863         // not sure why we use documentElement here.. - it should always be body.
14864         // IE7 borks horribly if we use documentElement.
14865         // webkit also does not like documentElement - it creates a body element...
14866         el = Roo.get( document.body || document.documentElement ).createChild();
14867         //config.autoCreate = true;
14868     }
14869     
14870     
14871     config.autoTabs = false;
14872     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
14873     this.body.setStyle({overflow:"hidden", position:"relative"});
14874     this.layout = new Roo.BorderLayout(this.body.dom, config);
14875     this.layout.monitorWindowResize = false;
14876     this.el.addClass("x-dlg-auto-layout");
14877     // fix case when center region overwrites center function
14878     this.center = Roo.BasicDialog.prototype.center;
14879     this.on("show", this.layout.layout, this.layout, true);
14880     if (config.items) {
14881         var xitems = config.items;
14882         delete config.items;
14883         Roo.each(xitems, this.addxtype, this);
14884     }
14885     
14886     
14887 };
14888 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
14889     /**
14890      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
14891      * @deprecated
14892      */
14893     endUpdate : function(){
14894         this.layout.endUpdate();
14895     },
14896
14897     /**
14898      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
14899      *  @deprecated
14900      */
14901     beginUpdate : function(){
14902         this.layout.beginUpdate();
14903     },
14904
14905     /**
14906      * Get the BorderLayout for this dialog
14907      * @return {Roo.BorderLayout}
14908      */
14909     getLayout : function(){
14910         return this.layout;
14911     },
14912
14913     showEl : function(){
14914         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
14915         if(Roo.isIE7){
14916             this.layout.layout();
14917         }
14918     },
14919
14920     // private
14921     // Use the syncHeightBeforeShow config option to control this automatically
14922     syncBodyHeight : function(){
14923         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
14924         if(this.layout){this.layout.layout();}
14925     },
14926     
14927       /**
14928      * Add an xtype element (actually adds to the layout.)
14929      * @return {Object} xdata xtype object data.
14930      */
14931     
14932     addxtype : function(c) {
14933         return this.layout.addxtype(c);
14934     }
14935 });/*
14936  * Based on:
14937  * Ext JS Library 1.1.1
14938  * Copyright(c) 2006-2007, Ext JS, LLC.
14939  *
14940  * Originally Released Under LGPL - original licence link has changed is not relivant.
14941  *
14942  * Fork - LGPL
14943  * <script type="text/javascript">
14944  */
14945  
14946 /**
14947  * @class Roo.MessageBox
14948  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
14949  * Example usage:
14950  *<pre><code>
14951 // Basic alert:
14952 Roo.Msg.alert('Status', 'Changes saved successfully.');
14953
14954 // Prompt for user data:
14955 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
14956     if (btn == 'ok'){
14957         // process text value...
14958     }
14959 });
14960
14961 // Show a dialog using config options:
14962 Roo.Msg.show({
14963    title:'Save Changes?',
14964    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
14965    buttons: Roo.Msg.YESNOCANCEL,
14966    fn: processResult,
14967    animEl: 'elId'
14968 });
14969 </code></pre>
14970  * @singleton
14971  */
14972 Roo.MessageBox = function(){
14973     var dlg, opt, mask, waitTimer;
14974     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
14975     var buttons, activeTextEl, bwidth;
14976
14977     // private
14978     var handleButton = function(button){
14979         dlg.hide();
14980         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
14981     };
14982
14983     // private
14984     var handleHide = function(){
14985         if(opt && opt.cls){
14986             dlg.el.removeClass(opt.cls);
14987         }
14988         if(waitTimer){
14989             Roo.TaskMgr.stop(waitTimer);
14990             waitTimer = null;
14991         }
14992     };
14993
14994     // private
14995     var updateButtons = function(b){
14996         var width = 0;
14997         if(!b){
14998             buttons["ok"].hide();
14999             buttons["cancel"].hide();
15000             buttons["yes"].hide();
15001             buttons["no"].hide();
15002             dlg.footer.dom.style.display = 'none';
15003             return width;
15004         }
15005         dlg.footer.dom.style.display = '';
15006         for(var k in buttons){
15007             if(typeof buttons[k] != "function"){
15008                 if(b[k]){
15009                     buttons[k].show();
15010                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15011                     width += buttons[k].el.getWidth()+15;
15012                 }else{
15013                     buttons[k].hide();
15014                 }
15015             }
15016         }
15017         return width;
15018     };
15019
15020     // private
15021     var handleEsc = function(d, k, e){
15022         if(opt && opt.closable !== false){
15023             dlg.hide();
15024         }
15025         if(e){
15026             e.stopEvent();
15027         }
15028     };
15029
15030     return {
15031         /**
15032          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15033          * @return {Roo.BasicDialog} The BasicDialog element
15034          */
15035         getDialog : function(){
15036            if(!dlg){
15037                 dlg = new Roo.BasicDialog("x-msg-box", {
15038                     autoCreate : true,
15039                     shadow: true,
15040                     draggable: true,
15041                     resizable:false,
15042                     constraintoviewport:false,
15043                     fixedcenter:true,
15044                     collapsible : false,
15045                     shim:true,
15046                     modal: true,
15047                     width:400, height:100,
15048                     buttonAlign:"center",
15049                     closeClick : function(){
15050                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15051                             handleButton("no");
15052                         }else{
15053                             handleButton("cancel");
15054                         }
15055                     }
15056                 });
15057                 dlg.on("hide", handleHide);
15058                 mask = dlg.mask;
15059                 dlg.addKeyListener(27, handleEsc);
15060                 buttons = {};
15061                 var bt = this.buttonText;
15062                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15063                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15064                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15065                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15066                 bodyEl = dlg.body.createChild({
15067
15068                     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>'
15069                 });
15070                 msgEl = bodyEl.dom.firstChild;
15071                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15072                 textboxEl.enableDisplayMode();
15073                 textboxEl.addKeyListener([10,13], function(){
15074                     if(dlg.isVisible() && opt && opt.buttons){
15075                         if(opt.buttons.ok){
15076                             handleButton("ok");
15077                         }else if(opt.buttons.yes){
15078                             handleButton("yes");
15079                         }
15080                     }
15081                 });
15082                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15083                 textareaEl.enableDisplayMode();
15084                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15085                 progressEl.enableDisplayMode();
15086                 var pf = progressEl.dom.firstChild;
15087                 if (pf) {
15088                     pp = Roo.get(pf.firstChild);
15089                     pp.setHeight(pf.offsetHeight);
15090                 }
15091                 
15092             }
15093             return dlg;
15094         },
15095
15096         /**
15097          * Updates the message box body text
15098          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15099          * the XHTML-compliant non-breaking space character '&amp;#160;')
15100          * @return {Roo.MessageBox} This message box
15101          */
15102         updateText : function(text){
15103             if(!dlg.isVisible() && !opt.width){
15104                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15105             }
15106             msgEl.innerHTML = text || '&#160;';
15107       
15108             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15109             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15110             var w = Math.max(
15111                     Math.min(opt.width || cw , this.maxWidth), 
15112                     Math.max(opt.minWidth || this.minWidth, bwidth)
15113             );
15114             if(opt.prompt){
15115                 activeTextEl.setWidth(w);
15116             }
15117             if(dlg.isVisible()){
15118                 dlg.fixedcenter = false;
15119             }
15120             // to big, make it scroll. = But as usual stupid IE does not support
15121             // !important..
15122             
15123             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15124                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15125                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15126             } else {
15127                 bodyEl.dom.style.height = '';
15128                 bodyEl.dom.style.overflowY = '';
15129             }
15130             if (cw > w) {
15131                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15132             } else {
15133                 bodyEl.dom.style.overflowX = '';
15134             }
15135             
15136             dlg.setContentSize(w, bodyEl.getHeight());
15137             if(dlg.isVisible()){
15138                 dlg.fixedcenter = true;
15139             }
15140             return this;
15141         },
15142
15143         /**
15144          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15145          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15146          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15147          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15148          * @return {Roo.MessageBox} This message box
15149          */
15150         updateProgress : function(value, text){
15151             if(text){
15152                 this.updateText(text);
15153             }
15154             if (pp) { // weird bug on my firefox - for some reason this is not defined
15155                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15156             }
15157             return this;
15158         },        
15159
15160         /**
15161          * Returns true if the message box is currently displayed
15162          * @return {Boolean} True if the message box is visible, else false
15163          */
15164         isVisible : function(){
15165             return dlg && dlg.isVisible();  
15166         },
15167
15168         /**
15169          * Hides the message box if it is displayed
15170          */
15171         hide : function(){
15172             if(this.isVisible()){
15173                 dlg.hide();
15174             }  
15175         },
15176
15177         /**
15178          * Displays a new message box, or reinitializes an existing message box, based on the config options
15179          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15180          * The following config object properties are supported:
15181          * <pre>
15182 Property    Type             Description
15183 ----------  ---------------  ------------------------------------------------------------------------------------
15184 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15185                                    closes (defaults to undefined)
15186 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15187                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15188 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15189                                    progress and wait dialogs will ignore this property and always hide the
15190                                    close button as they can only be closed programmatically.
15191 cls               String           A custom CSS class to apply to the message box element
15192 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15193                                    displayed (defaults to 75)
15194 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15195                                    function will be btn (the name of the button that was clicked, if applicable,
15196                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15197                                    Progress and wait dialogs will ignore this option since they do not respond to
15198                                    user actions and can only be closed programmatically, so any required function
15199                                    should be called by the same code after it closes the dialog.
15200 icon              String           A CSS class that provides a background image to be used as an icon for
15201                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15202 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15203 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15204 modal             Boolean          False to allow user interaction with the page while the message box is
15205                                    displayed (defaults to true)
15206 msg               String           A string that will replace the existing message box body text (defaults
15207                                    to the XHTML-compliant non-breaking space character '&#160;')
15208 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15209 progress          Boolean          True to display a progress bar (defaults to false)
15210 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15211 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15212 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15213 title             String           The title text
15214 value             String           The string value to set into the active textbox element if displayed
15215 wait              Boolean          True to display a progress bar (defaults to false)
15216 width             Number           The width of the dialog in pixels
15217 </pre>
15218          *
15219          * Example usage:
15220          * <pre><code>
15221 Roo.Msg.show({
15222    title: 'Address',
15223    msg: 'Please enter your address:',
15224    width: 300,
15225    buttons: Roo.MessageBox.OKCANCEL,
15226    multiline: true,
15227    fn: saveAddress,
15228    animEl: 'addAddressBtn'
15229 });
15230 </code></pre>
15231          * @param {Object} config Configuration options
15232          * @return {Roo.MessageBox} This message box
15233          */
15234         show : function(options)
15235         {
15236             
15237             // this causes nightmares if you show one dialog after another
15238             // especially on callbacks..
15239              
15240             if(this.isVisible()){
15241                 
15242                 this.hide();
15243                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15244                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
15245                 Roo.log("New Dialog Message:" +  options.msg )
15246                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15247                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15248                 
15249             }
15250             var d = this.getDialog();
15251             opt = options;
15252             d.setTitle(opt.title || "&#160;");
15253             d.close.setDisplayed(opt.closable !== false);
15254             activeTextEl = textboxEl;
15255             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15256             if(opt.prompt){
15257                 if(opt.multiline){
15258                     textboxEl.hide();
15259                     textareaEl.show();
15260                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15261                         opt.multiline : this.defaultTextHeight);
15262                     activeTextEl = textareaEl;
15263                 }else{
15264                     textboxEl.show();
15265                     textareaEl.hide();
15266                 }
15267             }else{
15268                 textboxEl.hide();
15269                 textareaEl.hide();
15270             }
15271             progressEl.setDisplayed(opt.progress === true);
15272             this.updateProgress(0);
15273             activeTextEl.dom.value = opt.value || "";
15274             if(opt.prompt){
15275                 dlg.setDefaultButton(activeTextEl);
15276             }else{
15277                 var bs = opt.buttons;
15278                 var db = null;
15279                 if(bs && bs.ok){
15280                     db = buttons["ok"];
15281                 }else if(bs && bs.yes){
15282                     db = buttons["yes"];
15283                 }
15284                 dlg.setDefaultButton(db);
15285             }
15286             bwidth = updateButtons(opt.buttons);
15287             this.updateText(opt.msg);
15288             if(opt.cls){
15289                 d.el.addClass(opt.cls);
15290             }
15291             d.proxyDrag = opt.proxyDrag === true;
15292             d.modal = opt.modal !== false;
15293             d.mask = opt.modal !== false ? mask : false;
15294             if(!d.isVisible()){
15295                 // force it to the end of the z-index stack so it gets a cursor in FF
15296                 document.body.appendChild(dlg.el.dom);
15297                 d.animateTarget = null;
15298                 d.show(options.animEl);
15299             }
15300             return this;
15301         },
15302
15303         /**
15304          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15305          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15306          * and closing the message box when the process is complete.
15307          * @param {String} title The title bar text
15308          * @param {String} msg The message box body text
15309          * @return {Roo.MessageBox} This message box
15310          */
15311         progress : function(title, msg){
15312             this.show({
15313                 title : title,
15314                 msg : msg,
15315                 buttons: false,
15316                 progress:true,
15317                 closable:false,
15318                 minWidth: this.minProgressWidth,
15319                 modal : true
15320             });
15321             return this;
15322         },
15323
15324         /**
15325          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15326          * If a callback function is passed it will be called after the user clicks the button, and the
15327          * id of the button that was clicked will be passed as the only parameter to the callback
15328          * (could also be the top-right close button).
15329          * @param {String} title The title bar text
15330          * @param {String} msg The message box body text
15331          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15332          * @param {Object} scope (optional) The scope of the callback function
15333          * @return {Roo.MessageBox} This message box
15334          */
15335         alert : function(title, msg, fn, scope){
15336             this.show({
15337                 title : title,
15338                 msg : msg,
15339                 buttons: this.OK,
15340                 fn: fn,
15341                 scope : scope,
15342                 modal : true
15343             });
15344             return this;
15345         },
15346
15347         /**
15348          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15349          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15350          * You are responsible for closing the message box when the process is complete.
15351          * @param {String} msg The message box body text
15352          * @param {String} title (optional) The title bar text
15353          * @return {Roo.MessageBox} This message box
15354          */
15355         wait : function(msg, title){
15356             this.show({
15357                 title : title,
15358                 msg : msg,
15359                 buttons: false,
15360                 closable:false,
15361                 progress:true,
15362                 modal:true,
15363                 width:300,
15364                 wait:true
15365             });
15366             waitTimer = Roo.TaskMgr.start({
15367                 run: function(i){
15368                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15369                 },
15370                 interval: 1000
15371             });
15372             return this;
15373         },
15374
15375         /**
15376          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15377          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15378          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15379          * @param {String} title The title bar text
15380          * @param {String} msg The message box body text
15381          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15382          * @param {Object} scope (optional) The scope of the callback function
15383          * @return {Roo.MessageBox} This message box
15384          */
15385         confirm : function(title, msg, fn, scope){
15386             this.show({
15387                 title : title,
15388                 msg : msg,
15389                 buttons: this.YESNO,
15390                 fn: fn,
15391                 scope : scope,
15392                 modal : true
15393             });
15394             return this;
15395         },
15396
15397         /**
15398          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15399          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15400          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15401          * (could also be the top-right close button) and the text that was entered will be passed as the two
15402          * parameters to the callback.
15403          * @param {String} title The title bar text
15404          * @param {String} msg The message box body text
15405          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15406          * @param {Object} scope (optional) The scope of the callback function
15407          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15408          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15409          * @return {Roo.MessageBox} This message box
15410          */
15411         prompt : function(title, msg, fn, scope, multiline){
15412             this.show({
15413                 title : title,
15414                 msg : msg,
15415                 buttons: this.OKCANCEL,
15416                 fn: fn,
15417                 minWidth:250,
15418                 scope : scope,
15419                 prompt:true,
15420                 multiline: multiline,
15421                 modal : true
15422             });
15423             return this;
15424         },
15425
15426         /**
15427          * Button config that displays a single OK button
15428          * @type Object
15429          */
15430         OK : {ok:true},
15431         /**
15432          * Button config that displays Yes and No buttons
15433          * @type Object
15434          */
15435         YESNO : {yes:true, no:true},
15436         /**
15437          * Button config that displays OK and Cancel buttons
15438          * @type Object
15439          */
15440         OKCANCEL : {ok:true, cancel:true},
15441         /**
15442          * Button config that displays Yes, No and Cancel buttons
15443          * @type Object
15444          */
15445         YESNOCANCEL : {yes:true, no:true, cancel:true},
15446
15447         /**
15448          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15449          * @type Number
15450          */
15451         defaultTextHeight : 75,
15452         /**
15453          * The maximum width in pixels of the message box (defaults to 600)
15454          * @type Number
15455          */
15456         maxWidth : 600,
15457         /**
15458          * The minimum width in pixels of the message box (defaults to 100)
15459          * @type Number
15460          */
15461         minWidth : 100,
15462         /**
15463          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15464          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15465          * @type Number
15466          */
15467         minProgressWidth : 250,
15468         /**
15469          * An object containing the default button text strings that can be overriden for localized language support.
15470          * Supported properties are: ok, cancel, yes and no.
15471          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15472          * @type Object
15473          */
15474         buttonText : {
15475             ok : "OK",
15476             cancel : "Cancel",
15477             yes : "Yes",
15478             no : "No"
15479         }
15480     };
15481 }();
15482
15483 /**
15484  * Shorthand for {@link Roo.MessageBox}
15485  */
15486 Roo.Msg = Roo.MessageBox;/*
15487  * Based on:
15488  * Ext JS Library 1.1.1
15489  * Copyright(c) 2006-2007, Ext JS, LLC.
15490  *
15491  * Originally Released Under LGPL - original licence link has changed is not relivant.
15492  *
15493  * Fork - LGPL
15494  * <script type="text/javascript">
15495  */
15496 /**
15497  * @class Roo.QuickTips
15498  * Provides attractive and customizable tooltips for any element.
15499  * @singleton
15500  */
15501 Roo.QuickTips = function(){
15502     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15503     var ce, bd, xy, dd;
15504     var visible = false, disabled = true, inited = false;
15505     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15506     
15507     var onOver = function(e){
15508         if(disabled){
15509             return;
15510         }
15511         var t = e.getTarget();
15512         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15513             return;
15514         }
15515         if(ce && t == ce.el){
15516             clearTimeout(hideProc);
15517             return;
15518         }
15519         if(t && tagEls[t.id]){
15520             tagEls[t.id].el = t;
15521             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15522             return;
15523         }
15524         var ttp, et = Roo.fly(t);
15525         var ns = cfg.namespace;
15526         if(tm.interceptTitles && t.title){
15527             ttp = t.title;
15528             t.qtip = ttp;
15529             t.removeAttribute("title");
15530             e.preventDefault();
15531         }else{
15532             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
15533         }
15534         if(ttp){
15535             showProc = show.defer(tm.showDelay, tm, [{
15536                 el: t, 
15537                 text: ttp, 
15538                 width: et.getAttributeNS(ns, cfg.width),
15539                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15540                 title: et.getAttributeNS(ns, cfg.title),
15541                     cls: et.getAttributeNS(ns, cfg.cls)
15542             }]);
15543         }
15544     };
15545     
15546     var onOut = function(e){
15547         clearTimeout(showProc);
15548         var t = e.getTarget();
15549         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15550             hideProc = setTimeout(hide, tm.hideDelay);
15551         }
15552     };
15553     
15554     var onMove = function(e){
15555         if(disabled){
15556             return;
15557         }
15558         xy = e.getXY();
15559         xy[1] += 18;
15560         if(tm.trackMouse && ce){
15561             el.setXY(xy);
15562         }
15563     };
15564     
15565     var onDown = function(e){
15566         clearTimeout(showProc);
15567         clearTimeout(hideProc);
15568         if(!e.within(el)){
15569             if(tm.hideOnClick){
15570                 hide();
15571                 tm.disable();
15572                 tm.enable.defer(100, tm);
15573             }
15574         }
15575     };
15576     
15577     var getPad = function(){
15578         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
15579     };
15580
15581     var show = function(o){
15582         if(disabled){
15583             return;
15584         }
15585         clearTimeout(dismissProc);
15586         ce = o;
15587         if(removeCls){ // in case manually hidden
15588             el.removeClass(removeCls);
15589             removeCls = null;
15590         }
15591         if(ce.cls){
15592             el.addClass(ce.cls);
15593             removeCls = ce.cls;
15594         }
15595         if(ce.title){
15596             tipTitle.update(ce.title);
15597             tipTitle.show();
15598         }else{
15599             tipTitle.update('');
15600             tipTitle.hide();
15601         }
15602         el.dom.style.width  = tm.maxWidth+'px';
15603         //tipBody.dom.style.width = '';
15604         tipBodyText.update(o.text);
15605         var p = getPad(), w = ce.width;
15606         if(!w){
15607             var td = tipBodyText.dom;
15608             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
15609             if(aw > tm.maxWidth){
15610                 w = tm.maxWidth;
15611             }else if(aw < tm.minWidth){
15612                 w = tm.minWidth;
15613             }else{
15614                 w = aw;
15615             }
15616         }
15617         //tipBody.setWidth(w);
15618         el.setWidth(parseInt(w, 10) + p);
15619         if(ce.autoHide === false){
15620             close.setDisplayed(true);
15621             if(dd){
15622                 dd.unlock();
15623             }
15624         }else{
15625             close.setDisplayed(false);
15626             if(dd){
15627                 dd.lock();
15628             }
15629         }
15630         if(xy){
15631             el.avoidY = xy[1]-18;
15632             el.setXY(xy);
15633         }
15634         if(tm.animate){
15635             el.setOpacity(.1);
15636             el.setStyle("visibility", "visible");
15637             el.fadeIn({callback: afterShow});
15638         }else{
15639             afterShow();
15640         }
15641     };
15642     
15643     var afterShow = function(){
15644         if(ce){
15645             el.show();
15646             esc.enable();
15647             if(tm.autoDismiss && ce.autoHide !== false){
15648                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
15649             }
15650         }
15651     };
15652     
15653     var hide = function(noanim){
15654         clearTimeout(dismissProc);
15655         clearTimeout(hideProc);
15656         ce = null;
15657         if(el.isVisible()){
15658             esc.disable();
15659             if(noanim !== true && tm.animate){
15660                 el.fadeOut({callback: afterHide});
15661             }else{
15662                 afterHide();
15663             } 
15664         }
15665     };
15666     
15667     var afterHide = function(){
15668         el.hide();
15669         if(removeCls){
15670             el.removeClass(removeCls);
15671             removeCls = null;
15672         }
15673     };
15674     
15675     return {
15676         /**
15677         * @cfg {Number} minWidth
15678         * The minimum width of the quick tip (defaults to 40)
15679         */
15680        minWidth : 40,
15681         /**
15682         * @cfg {Number} maxWidth
15683         * The maximum width of the quick tip (defaults to 300)
15684         */
15685        maxWidth : 300,
15686         /**
15687         * @cfg {Boolean} interceptTitles
15688         * True to automatically use the element's DOM title value if available (defaults to false)
15689         */
15690        interceptTitles : false,
15691         /**
15692         * @cfg {Boolean} trackMouse
15693         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
15694         */
15695        trackMouse : false,
15696         /**
15697         * @cfg {Boolean} hideOnClick
15698         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
15699         */
15700        hideOnClick : true,
15701         /**
15702         * @cfg {Number} showDelay
15703         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
15704         */
15705        showDelay : 500,
15706         /**
15707         * @cfg {Number} hideDelay
15708         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
15709         */
15710        hideDelay : 200,
15711         /**
15712         * @cfg {Boolean} autoHide
15713         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
15714         * Used in conjunction with hideDelay.
15715         */
15716        autoHide : true,
15717         /**
15718         * @cfg {Boolean}
15719         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
15720         * (defaults to true).  Used in conjunction with autoDismissDelay.
15721         */
15722        autoDismiss : true,
15723         /**
15724         * @cfg {Number}
15725         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
15726         */
15727        autoDismissDelay : 5000,
15728        /**
15729         * @cfg {Boolean} animate
15730         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
15731         */
15732        animate : false,
15733
15734        /**
15735         * @cfg {String} title
15736         * Title text to display (defaults to '').  This can be any valid HTML markup.
15737         */
15738         title: '',
15739        /**
15740         * @cfg {String} text
15741         * Body text to display (defaults to '').  This can be any valid HTML markup.
15742         */
15743         text : '',
15744        /**
15745         * @cfg {String} cls
15746         * A CSS class to apply to the base quick tip element (defaults to '').
15747         */
15748         cls : '',
15749        /**
15750         * @cfg {Number} width
15751         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
15752         * minWidth or maxWidth.
15753         */
15754         width : null,
15755
15756     /**
15757      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
15758      * or display QuickTips in a page.
15759      */
15760        init : function(){
15761           tm = Roo.QuickTips;
15762           cfg = tm.tagConfig;
15763           if(!inited){
15764               if(!Roo.isReady){ // allow calling of init() before onReady
15765                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
15766                   return;
15767               }
15768               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
15769               el.fxDefaults = {stopFx: true};
15770               // maximum custom styling
15771               //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>');
15772               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>');              
15773               tipTitle = el.child('h3');
15774               tipTitle.enableDisplayMode("block");
15775               tipBody = el.child('div.x-tip-bd');
15776               tipBodyText = el.child('div.x-tip-bd-inner');
15777               //bdLeft = el.child('div.x-tip-bd-left');
15778               //bdRight = el.child('div.x-tip-bd-right');
15779               close = el.child('div.x-tip-close');
15780               close.enableDisplayMode("block");
15781               close.on("click", hide);
15782               var d = Roo.get(document);
15783               d.on("mousedown", onDown);
15784               d.on("mouseover", onOver);
15785               d.on("mouseout", onOut);
15786               d.on("mousemove", onMove);
15787               esc = d.addKeyListener(27, hide);
15788               esc.disable();
15789               if(Roo.dd.DD){
15790                   dd = el.initDD("default", null, {
15791                       onDrag : function(){
15792                           el.sync();  
15793                       }
15794                   });
15795                   dd.setHandleElId(tipTitle.id);
15796                   dd.lock();
15797               }
15798               inited = true;
15799           }
15800           this.enable(); 
15801        },
15802
15803     /**
15804      * Configures a new quick tip instance and assigns it to a target element.  The following config options
15805      * are supported:
15806      * <pre>
15807 Property    Type                   Description
15808 ----------  ---------------------  ------------------------------------------------------------------------
15809 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
15810      * </ul>
15811      * @param {Object} config The config object
15812      */
15813        register : function(config){
15814            var cs = config instanceof Array ? config : arguments;
15815            for(var i = 0, len = cs.length; i < len; i++) {
15816                var c = cs[i];
15817                var target = c.target;
15818                if(target){
15819                    if(target instanceof Array){
15820                        for(var j = 0, jlen = target.length; j < jlen; j++){
15821                            tagEls[target[j]] = c;
15822                        }
15823                    }else{
15824                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
15825                    }
15826                }
15827            }
15828        },
15829
15830     /**
15831      * Removes this quick tip from its element and destroys it.
15832      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
15833      */
15834        unregister : function(el){
15835            delete tagEls[Roo.id(el)];
15836        },
15837
15838     /**
15839      * Enable this quick tip.
15840      */
15841        enable : function(){
15842            if(inited && disabled){
15843                locks.pop();
15844                if(locks.length < 1){
15845                    disabled = false;
15846                }
15847            }
15848        },
15849
15850     /**
15851      * Disable this quick tip.
15852      */
15853        disable : function(){
15854           disabled = true;
15855           clearTimeout(showProc);
15856           clearTimeout(hideProc);
15857           clearTimeout(dismissProc);
15858           if(ce){
15859               hide(true);
15860           }
15861           locks.push(1);
15862        },
15863
15864     /**
15865      * Returns true if the quick tip is enabled, else false.
15866      */
15867        isEnabled : function(){
15868             return !disabled;
15869        },
15870
15871         // private
15872        tagConfig : {
15873            namespace : "ext",
15874            attribute : "qtip",
15875            width : "width",
15876            target : "target",
15877            title : "qtitle",
15878            hide : "hide",
15879            cls : "qclass"
15880        }
15881    };
15882 }();
15883
15884 // backwards compat
15885 Roo.QuickTips.tips = Roo.QuickTips.register;/*
15886  * Based on:
15887  * Ext JS Library 1.1.1
15888  * Copyright(c) 2006-2007, Ext JS, LLC.
15889  *
15890  * Originally Released Under LGPL - original licence link has changed is not relivant.
15891  *
15892  * Fork - LGPL
15893  * <script type="text/javascript">
15894  */
15895  
15896
15897 /**
15898  * @class Roo.tree.TreePanel
15899  * @extends Roo.data.Tree
15900
15901  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
15902  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
15903  * @cfg {Boolean} enableDD true to enable drag and drop
15904  * @cfg {Boolean} enableDrag true to enable just drag
15905  * @cfg {Boolean} enableDrop true to enable just drop
15906  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
15907  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
15908  * @cfg {String} ddGroup The DD group this TreePanel belongs to
15909  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
15910  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
15911  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
15912  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
15913  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
15914  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
15915  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
15916  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
15917  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
15918  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
15919  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
15920  * @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>
15921  * @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>
15922  * 
15923  * @constructor
15924  * @param {String/HTMLElement/Element} el The container element
15925  * @param {Object} config
15926  */
15927 Roo.tree.TreePanel = function(el, config){
15928     var root = false;
15929     var loader = false;
15930     if (config.root) {
15931         root = config.root;
15932         delete config.root;
15933     }
15934     if (config.loader) {
15935         loader = config.loader;
15936         delete config.loader;
15937     }
15938     
15939     Roo.apply(this, config);
15940     Roo.tree.TreePanel.superclass.constructor.call(this);
15941     this.el = Roo.get(el);
15942     this.el.addClass('x-tree');
15943     //console.log(root);
15944     if (root) {
15945         this.setRootNode( Roo.factory(root, Roo.tree));
15946     }
15947     if (loader) {
15948         this.loader = Roo.factory(loader, Roo.tree);
15949     }
15950    /**
15951     * Read-only. The id of the container element becomes this TreePanel's id.
15952     */
15953     this.id = this.el.id;
15954     this.addEvents({
15955         /**
15956         * @event beforeload
15957         * Fires before a node is loaded, return false to cancel
15958         * @param {Node} node The node being loaded
15959         */
15960         "beforeload" : true,
15961         /**
15962         * @event load
15963         * Fires when a node is loaded
15964         * @param {Node} node The node that was loaded
15965         */
15966         "load" : true,
15967         /**
15968         * @event textchange
15969         * Fires when the text for a node is changed
15970         * @param {Node} node The node
15971         * @param {String} text The new text
15972         * @param {String} oldText The old text
15973         */
15974         "textchange" : true,
15975         /**
15976         * @event beforeexpand
15977         * Fires before a node is expanded, return false to cancel.
15978         * @param {Node} node The node
15979         * @param {Boolean} deep
15980         * @param {Boolean} anim
15981         */
15982         "beforeexpand" : true,
15983         /**
15984         * @event beforecollapse
15985         * Fires before a node is collapsed, return false to cancel.
15986         * @param {Node} node The node
15987         * @param {Boolean} deep
15988         * @param {Boolean} anim
15989         */
15990         "beforecollapse" : true,
15991         /**
15992         * @event expand
15993         * Fires when a node is expanded
15994         * @param {Node} node The node
15995         */
15996         "expand" : true,
15997         /**
15998         * @event disabledchange
15999         * Fires when the disabled status of a node changes
16000         * @param {Node} node The node
16001         * @param {Boolean} disabled
16002         */
16003         "disabledchange" : true,
16004         /**
16005         * @event collapse
16006         * Fires when a node is collapsed
16007         * @param {Node} node The node
16008         */
16009         "collapse" : true,
16010         /**
16011         * @event beforeclick
16012         * Fires before click processing on a node. Return false to cancel the default action.
16013         * @param {Node} node The node
16014         * @param {Roo.EventObject} e The event object
16015         */
16016         "beforeclick":true,
16017         /**
16018         * @event checkchange
16019         * Fires when a node with a checkbox's checked property changes
16020         * @param {Node} this This node
16021         * @param {Boolean} checked
16022         */
16023         "checkchange":true,
16024         /**
16025         * @event click
16026         * Fires when a node is clicked
16027         * @param {Node} node The node
16028         * @param {Roo.EventObject} e The event object
16029         */
16030         "click":true,
16031         /**
16032         * @event dblclick
16033         * Fires when a node is double clicked
16034         * @param {Node} node The node
16035         * @param {Roo.EventObject} e The event object
16036         */
16037         "dblclick":true,
16038         /**
16039         * @event contextmenu
16040         * Fires when a node is right clicked
16041         * @param {Node} node The node
16042         * @param {Roo.EventObject} e The event object
16043         */
16044         "contextmenu":true,
16045         /**
16046         * @event beforechildrenrendered
16047         * Fires right before the child nodes for a node are rendered
16048         * @param {Node} node The node
16049         */
16050         "beforechildrenrendered":true,
16051         /**
16052         * @event startdrag
16053         * Fires when a node starts being dragged
16054         * @param {Roo.tree.TreePanel} this
16055         * @param {Roo.tree.TreeNode} node
16056         * @param {event} e The raw browser event
16057         */ 
16058        "startdrag" : true,
16059        /**
16060         * @event enddrag
16061         * Fires when a drag operation is complete
16062         * @param {Roo.tree.TreePanel} this
16063         * @param {Roo.tree.TreeNode} node
16064         * @param {event} e The raw browser event
16065         */
16066        "enddrag" : true,
16067        /**
16068         * @event dragdrop
16069         * Fires when a dragged node is dropped on a valid DD target
16070         * @param {Roo.tree.TreePanel} this
16071         * @param {Roo.tree.TreeNode} node
16072         * @param {DD} dd The dd it was dropped on
16073         * @param {event} e The raw browser event
16074         */
16075        "dragdrop" : true,
16076        /**
16077         * @event beforenodedrop
16078         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16079         * passed to handlers has the following properties:<br />
16080         * <ul style="padding:5px;padding-left:16px;">
16081         * <li>tree - The TreePanel</li>
16082         * <li>target - The node being targeted for the drop</li>
16083         * <li>data - The drag data from the drag source</li>
16084         * <li>point - The point of the drop - append, above or below</li>
16085         * <li>source - The drag source</li>
16086         * <li>rawEvent - Raw mouse event</li>
16087         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16088         * to be inserted by setting them on this object.</li>
16089         * <li>cancel - Set this to true to cancel the drop.</li>
16090         * </ul>
16091         * @param {Object} dropEvent
16092         */
16093        "beforenodedrop" : true,
16094        /**
16095         * @event nodedrop
16096         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16097         * passed to handlers has the following properties:<br />
16098         * <ul style="padding:5px;padding-left:16px;">
16099         * <li>tree - The TreePanel</li>
16100         * <li>target - The node being targeted for the drop</li>
16101         * <li>data - The drag data from the drag source</li>
16102         * <li>point - The point of the drop - append, above or below</li>
16103         * <li>source - The drag source</li>
16104         * <li>rawEvent - Raw mouse event</li>
16105         * <li>dropNode - Dropped node(s).</li>
16106         * </ul>
16107         * @param {Object} dropEvent
16108         */
16109        "nodedrop" : true,
16110         /**
16111         * @event nodedragover
16112         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16113         * passed to handlers has the following properties:<br />
16114         * <ul style="padding:5px;padding-left:16px;">
16115         * <li>tree - The TreePanel</li>
16116         * <li>target - The node being targeted for the drop</li>
16117         * <li>data - The drag data from the drag source</li>
16118         * <li>point - The point of the drop - append, above or below</li>
16119         * <li>source - The drag source</li>
16120         * <li>rawEvent - Raw mouse event</li>
16121         * <li>dropNode - Drop node(s) provided by the source.</li>
16122         * <li>cancel - Set this to true to signal drop not allowed.</li>
16123         * </ul>
16124         * @param {Object} dragOverEvent
16125         */
16126        "nodedragover" : true
16127         
16128     });
16129     if(this.singleExpand){
16130        this.on("beforeexpand", this.restrictExpand, this);
16131     }
16132     if (this.editor) {
16133         this.editor.tree = this;
16134         this.editor = Roo.factory(this.editor, Roo.tree);
16135     }
16136     
16137     if (this.selModel) {
16138         this.selModel = Roo.factory(this.selModel, Roo.tree);
16139     }
16140    
16141 };
16142 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16143     rootVisible : true,
16144     animate: Roo.enableFx,
16145     lines : true,
16146     enableDD : false,
16147     hlDrop : Roo.enableFx,
16148   
16149     renderer: false,
16150     
16151     rendererTip: false,
16152     // private
16153     restrictExpand : function(node){
16154         var p = node.parentNode;
16155         if(p){
16156             if(p.expandedChild && p.expandedChild.parentNode == p){
16157                 p.expandedChild.collapse();
16158             }
16159             p.expandedChild = node;
16160         }
16161     },
16162
16163     // private override
16164     setRootNode : function(node){
16165         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16166         if(!this.rootVisible){
16167             node.ui = new Roo.tree.RootTreeNodeUI(node);
16168         }
16169         return node;
16170     },
16171
16172     /**
16173      * Returns the container element for this TreePanel
16174      */
16175     getEl : function(){
16176         return this.el;
16177     },
16178
16179     /**
16180      * Returns the default TreeLoader for this TreePanel
16181      */
16182     getLoader : function(){
16183         return this.loader;
16184     },
16185
16186     /**
16187      * Expand all nodes
16188      */
16189     expandAll : function(){
16190         this.root.expand(true);
16191     },
16192
16193     /**
16194      * Collapse all nodes
16195      */
16196     collapseAll : function(){
16197         this.root.collapse(true);
16198     },
16199
16200     /**
16201      * Returns the selection model used by this TreePanel
16202      */
16203     getSelectionModel : function(){
16204         if(!this.selModel){
16205             this.selModel = new Roo.tree.DefaultSelectionModel();
16206         }
16207         return this.selModel;
16208     },
16209
16210     /**
16211      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16212      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16213      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16214      * @return {Array}
16215      */
16216     getChecked : function(a, startNode){
16217         startNode = startNode || this.root;
16218         var r = [];
16219         var f = function(){
16220             if(this.attributes.checked){
16221                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16222             }
16223         }
16224         startNode.cascade(f);
16225         return r;
16226     },
16227
16228     /**
16229      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16230      * @param {String} path
16231      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16232      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16233      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16234      */
16235     expandPath : function(path, attr, callback){
16236         attr = attr || "id";
16237         var keys = path.split(this.pathSeparator);
16238         var curNode = this.root;
16239         if(curNode.attributes[attr] != keys[1]){ // invalid root
16240             if(callback){
16241                 callback(false, null);
16242             }
16243             return;
16244         }
16245         var index = 1;
16246         var f = function(){
16247             if(++index == keys.length){
16248                 if(callback){
16249                     callback(true, curNode);
16250                 }
16251                 return;
16252             }
16253             var c = curNode.findChild(attr, keys[index]);
16254             if(!c){
16255                 if(callback){
16256                     callback(false, curNode);
16257                 }
16258                 return;
16259             }
16260             curNode = c;
16261             c.expand(false, false, f);
16262         };
16263         curNode.expand(false, false, f);
16264     },
16265
16266     /**
16267      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16268      * @param {String} path
16269      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16270      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16271      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16272      */
16273     selectPath : function(path, attr, callback){
16274         attr = attr || "id";
16275         var keys = path.split(this.pathSeparator);
16276         var v = keys.pop();
16277         if(keys.length > 0){
16278             var f = function(success, node){
16279                 if(success && node){
16280                     var n = node.findChild(attr, v);
16281                     if(n){
16282                         n.select();
16283                         if(callback){
16284                             callback(true, n);
16285                         }
16286                     }else if(callback){
16287                         callback(false, n);
16288                     }
16289                 }else{
16290                     if(callback){
16291                         callback(false, n);
16292                     }
16293                 }
16294             };
16295             this.expandPath(keys.join(this.pathSeparator), attr, f);
16296         }else{
16297             this.root.select();
16298             if(callback){
16299                 callback(true, this.root);
16300             }
16301         }
16302     },
16303
16304     getTreeEl : function(){
16305         return this.el;
16306     },
16307
16308     /**
16309      * Trigger rendering of this TreePanel
16310      */
16311     render : function(){
16312         if (this.innerCt) {
16313             return this; // stop it rendering more than once!!
16314         }
16315         
16316         this.innerCt = this.el.createChild({tag:"ul",
16317                cls:"x-tree-root-ct " +
16318                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16319
16320         if(this.containerScroll){
16321             Roo.dd.ScrollManager.register(this.el);
16322         }
16323         if((this.enableDD || this.enableDrop) && !this.dropZone){
16324            /**
16325             * The dropZone used by this tree if drop is enabled
16326             * @type Roo.tree.TreeDropZone
16327             */
16328              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16329                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16330            });
16331         }
16332         if((this.enableDD || this.enableDrag) && !this.dragZone){
16333            /**
16334             * The dragZone used by this tree if drag is enabled
16335             * @type Roo.tree.TreeDragZone
16336             */
16337             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16338                ddGroup: this.ddGroup || "TreeDD",
16339                scroll: this.ddScroll
16340            });
16341         }
16342         this.getSelectionModel().init(this);
16343         if (!this.root) {
16344             Roo.log("ROOT not set in tree");
16345             return this;
16346         }
16347         this.root.render();
16348         if(!this.rootVisible){
16349             this.root.renderChildren();
16350         }
16351         return this;
16352     }
16353 });/*
16354  * Based on:
16355  * Ext JS Library 1.1.1
16356  * Copyright(c) 2006-2007, Ext JS, LLC.
16357  *
16358  * Originally Released Under LGPL - original licence link has changed is not relivant.
16359  *
16360  * Fork - LGPL
16361  * <script type="text/javascript">
16362  */
16363  
16364
16365 /**
16366  * @class Roo.tree.DefaultSelectionModel
16367  * @extends Roo.util.Observable
16368  * The default single selection for a TreePanel.
16369  * @param {Object} cfg Configuration
16370  */
16371 Roo.tree.DefaultSelectionModel = function(cfg){
16372    this.selNode = null;
16373    
16374    
16375    
16376    this.addEvents({
16377        /**
16378         * @event selectionchange
16379         * Fires when the selected node changes
16380         * @param {DefaultSelectionModel} this
16381         * @param {TreeNode} node the new selection
16382         */
16383        "selectionchange" : true,
16384
16385        /**
16386         * @event beforeselect
16387         * Fires before the selected node changes, return false to cancel the change
16388         * @param {DefaultSelectionModel} this
16389         * @param {TreeNode} node the new selection
16390         * @param {TreeNode} node the old selection
16391         */
16392        "beforeselect" : true
16393    });
16394    
16395     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
16396 };
16397
16398 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16399     init : function(tree){
16400         this.tree = tree;
16401         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16402         tree.on("click", this.onNodeClick, this);
16403     },
16404     
16405     onNodeClick : function(node, e){
16406         if (e.ctrlKey && this.selNode == node)  {
16407             this.unselect(node);
16408             return;
16409         }
16410         this.select(node);
16411     },
16412     
16413     /**
16414      * Select a node.
16415      * @param {TreeNode} node The node to select
16416      * @return {TreeNode} The selected node
16417      */
16418     select : function(node){
16419         var last = this.selNode;
16420         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16421             if(last){
16422                 last.ui.onSelectedChange(false);
16423             }
16424             this.selNode = node;
16425             node.ui.onSelectedChange(true);
16426             this.fireEvent("selectionchange", this, node, last);
16427         }
16428         return node;
16429     },
16430     
16431     /**
16432      * Deselect a node.
16433      * @param {TreeNode} node The node to unselect
16434      */
16435     unselect : function(node){
16436         if(this.selNode == node){
16437             this.clearSelections();
16438         }    
16439     },
16440     
16441     /**
16442      * Clear all selections
16443      */
16444     clearSelections : function(){
16445         var n = this.selNode;
16446         if(n){
16447             n.ui.onSelectedChange(false);
16448             this.selNode = null;
16449             this.fireEvent("selectionchange", this, null);
16450         }
16451         return n;
16452     },
16453     
16454     /**
16455      * Get the selected node
16456      * @return {TreeNode} The selected node
16457      */
16458     getSelectedNode : function(){
16459         return this.selNode;    
16460     },
16461     
16462     /**
16463      * Returns true if the node is selected
16464      * @param {TreeNode} node The node to check
16465      * @return {Boolean}
16466      */
16467     isSelected : function(node){
16468         return this.selNode == node;  
16469     },
16470
16471     /**
16472      * Selects the node above the selected node in the tree, intelligently walking the nodes
16473      * @return TreeNode The new selection
16474      */
16475     selectPrevious : function(){
16476         var s = this.selNode || this.lastSelNode;
16477         if(!s){
16478             return null;
16479         }
16480         var ps = s.previousSibling;
16481         if(ps){
16482             if(!ps.isExpanded() || ps.childNodes.length < 1){
16483                 return this.select(ps);
16484             } else{
16485                 var lc = ps.lastChild;
16486                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16487                     lc = lc.lastChild;
16488                 }
16489                 return this.select(lc);
16490             }
16491         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16492             return this.select(s.parentNode);
16493         }
16494         return null;
16495     },
16496
16497     /**
16498      * Selects the node above the selected node in the tree, intelligently walking the nodes
16499      * @return TreeNode The new selection
16500      */
16501     selectNext : function(){
16502         var s = this.selNode || this.lastSelNode;
16503         if(!s){
16504             return null;
16505         }
16506         if(s.firstChild && s.isExpanded()){
16507              return this.select(s.firstChild);
16508          }else if(s.nextSibling){
16509              return this.select(s.nextSibling);
16510          }else if(s.parentNode){
16511             var newS = null;
16512             s.parentNode.bubble(function(){
16513                 if(this.nextSibling){
16514                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16515                     return false;
16516                 }
16517             });
16518             return newS;
16519          }
16520         return null;
16521     },
16522
16523     onKeyDown : function(e){
16524         var s = this.selNode || this.lastSelNode;
16525         // undesirable, but required
16526         var sm = this;
16527         if(!s){
16528             return;
16529         }
16530         var k = e.getKey();
16531         switch(k){
16532              case e.DOWN:
16533                  e.stopEvent();
16534                  this.selectNext();
16535              break;
16536              case e.UP:
16537                  e.stopEvent();
16538                  this.selectPrevious();
16539              break;
16540              case e.RIGHT:
16541                  e.preventDefault();
16542                  if(s.hasChildNodes()){
16543                      if(!s.isExpanded()){
16544                          s.expand();
16545                      }else if(s.firstChild){
16546                          this.select(s.firstChild, e);
16547                      }
16548                  }
16549              break;
16550              case e.LEFT:
16551                  e.preventDefault();
16552                  if(s.hasChildNodes() && s.isExpanded()){
16553                      s.collapse();
16554                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16555                      this.select(s.parentNode, e);
16556                  }
16557              break;
16558         };
16559     }
16560 });
16561
16562 /**
16563  * @class Roo.tree.MultiSelectionModel
16564  * @extends Roo.util.Observable
16565  * Multi selection for a TreePanel.
16566  * @param {Object} cfg Configuration
16567  */
16568 Roo.tree.MultiSelectionModel = function(){
16569    this.selNodes = [];
16570    this.selMap = {};
16571    this.addEvents({
16572        /**
16573         * @event selectionchange
16574         * Fires when the selected nodes change
16575         * @param {MultiSelectionModel} this
16576         * @param {Array} nodes Array of the selected nodes
16577         */
16578        "selectionchange" : true
16579    });
16580    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
16581    
16582 };
16583
16584 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16585     init : function(tree){
16586         this.tree = tree;
16587         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16588         tree.on("click", this.onNodeClick, this);
16589     },
16590     
16591     onNodeClick : function(node, e){
16592         this.select(node, e, e.ctrlKey);
16593     },
16594     
16595     /**
16596      * Select a node.
16597      * @param {TreeNode} node The node to select
16598      * @param {EventObject} e (optional) An event associated with the selection
16599      * @param {Boolean} keepExisting True to retain existing selections
16600      * @return {TreeNode} The selected node
16601      */
16602     select : function(node, e, keepExisting){
16603         if(keepExisting !== true){
16604             this.clearSelections(true);
16605         }
16606         if(this.isSelected(node)){
16607             this.lastSelNode = node;
16608             return node;
16609         }
16610         this.selNodes.push(node);
16611         this.selMap[node.id] = node;
16612         this.lastSelNode = node;
16613         node.ui.onSelectedChange(true);
16614         this.fireEvent("selectionchange", this, this.selNodes);
16615         return node;
16616     },
16617     
16618     /**
16619      * Deselect a node.
16620      * @param {TreeNode} node The node to unselect
16621      */
16622     unselect : function(node){
16623         if(this.selMap[node.id]){
16624             node.ui.onSelectedChange(false);
16625             var sn = this.selNodes;
16626             var index = -1;
16627             if(sn.indexOf){
16628                 index = sn.indexOf(node);
16629             }else{
16630                 for(var i = 0, len = sn.length; i < len; i++){
16631                     if(sn[i] == node){
16632                         index = i;
16633                         break;
16634                     }
16635                 }
16636             }
16637             if(index != -1){
16638                 this.selNodes.splice(index, 1);
16639             }
16640             delete this.selMap[node.id];
16641             this.fireEvent("selectionchange", this, this.selNodes);
16642         }
16643     },
16644     
16645     /**
16646      * Clear all selections
16647      */
16648     clearSelections : function(suppressEvent){
16649         var sn = this.selNodes;
16650         if(sn.length > 0){
16651             for(var i = 0, len = sn.length; i < len; i++){
16652                 sn[i].ui.onSelectedChange(false);
16653             }
16654             this.selNodes = [];
16655             this.selMap = {};
16656             if(suppressEvent !== true){
16657                 this.fireEvent("selectionchange", this, this.selNodes);
16658             }
16659         }
16660     },
16661     
16662     /**
16663      * Returns true if the node is selected
16664      * @param {TreeNode} node The node to check
16665      * @return {Boolean}
16666      */
16667     isSelected : function(node){
16668         return this.selMap[node.id] ? true : false;  
16669     },
16670     
16671     /**
16672      * Returns an array of the selected nodes
16673      * @return {Array}
16674      */
16675     getSelectedNodes : function(){
16676         return this.selNodes;    
16677     },
16678
16679     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
16680
16681     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
16682
16683     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
16684 });/*
16685  * Based on:
16686  * Ext JS Library 1.1.1
16687  * Copyright(c) 2006-2007, Ext JS, LLC.
16688  *
16689  * Originally Released Under LGPL - original licence link has changed is not relivant.
16690  *
16691  * Fork - LGPL
16692  * <script type="text/javascript">
16693  */
16694  
16695 /**
16696  * @class Roo.tree.TreeNode
16697  * @extends Roo.data.Node
16698  * @cfg {String} text The text for this node
16699  * @cfg {Boolean} expanded true to start the node expanded
16700  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
16701  * @cfg {Boolean} allowDrop false if this node cannot be drop on
16702  * @cfg {Boolean} disabled true to start the node disabled
16703  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
16704  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
16705  * @cfg {String} cls A css class to be added to the node
16706  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
16707  * @cfg {String} href URL of the link used for the node (defaults to #)
16708  * @cfg {String} hrefTarget target frame for the link
16709  * @cfg {String} qtip An Ext QuickTip for the node
16710  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
16711  * @cfg {Boolean} singleClickExpand True for single click expand on this node
16712  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
16713  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
16714  * (defaults to undefined with no checkbox rendered)
16715  * @constructor
16716  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
16717  */
16718 Roo.tree.TreeNode = function(attributes){
16719     attributes = attributes || {};
16720     if(typeof attributes == "string"){
16721         attributes = {text: attributes};
16722     }
16723     this.childrenRendered = false;
16724     this.rendered = false;
16725     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
16726     this.expanded = attributes.expanded === true;
16727     this.isTarget = attributes.isTarget !== false;
16728     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
16729     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
16730
16731     /**
16732      * Read-only. The text for this node. To change it use setText().
16733      * @type String
16734      */
16735     this.text = attributes.text;
16736     /**
16737      * True if this node is disabled.
16738      * @type Boolean
16739      */
16740     this.disabled = attributes.disabled === true;
16741
16742     this.addEvents({
16743         /**
16744         * @event textchange
16745         * Fires when the text for this node is changed
16746         * @param {Node} this This node
16747         * @param {String} text The new text
16748         * @param {String} oldText The old text
16749         */
16750         "textchange" : true,
16751         /**
16752         * @event beforeexpand
16753         * Fires before this node is expanded, return false to cancel.
16754         * @param {Node} this This node
16755         * @param {Boolean} deep
16756         * @param {Boolean} anim
16757         */
16758         "beforeexpand" : true,
16759         /**
16760         * @event beforecollapse
16761         * Fires before this node is collapsed, return false to cancel.
16762         * @param {Node} this This node
16763         * @param {Boolean} deep
16764         * @param {Boolean} anim
16765         */
16766         "beforecollapse" : true,
16767         /**
16768         * @event expand
16769         * Fires when this node is expanded
16770         * @param {Node} this This node
16771         */
16772         "expand" : true,
16773         /**
16774         * @event disabledchange
16775         * Fires when the disabled status of this node changes
16776         * @param {Node} this This node
16777         * @param {Boolean} disabled
16778         */
16779         "disabledchange" : true,
16780         /**
16781         * @event collapse
16782         * Fires when this node is collapsed
16783         * @param {Node} this This node
16784         */
16785         "collapse" : true,
16786         /**
16787         * @event beforeclick
16788         * Fires before click processing. Return false to cancel the default action.
16789         * @param {Node} this This node
16790         * @param {Roo.EventObject} e The event object
16791         */
16792         "beforeclick":true,
16793         /**
16794         * @event checkchange
16795         * Fires when a node with a checkbox's checked property changes
16796         * @param {Node} this This node
16797         * @param {Boolean} checked
16798         */
16799         "checkchange":true,
16800         /**
16801         * @event click
16802         * Fires when this node is clicked
16803         * @param {Node} this This node
16804         * @param {Roo.EventObject} e The event object
16805         */
16806         "click":true,
16807         /**
16808         * @event dblclick
16809         * Fires when this node is double clicked
16810         * @param {Node} this This node
16811         * @param {Roo.EventObject} e The event object
16812         */
16813         "dblclick":true,
16814         /**
16815         * @event contextmenu
16816         * Fires when this node is right clicked
16817         * @param {Node} this This node
16818         * @param {Roo.EventObject} e The event object
16819         */
16820         "contextmenu":true,
16821         /**
16822         * @event beforechildrenrendered
16823         * Fires right before the child nodes for this node are rendered
16824         * @param {Node} this This node
16825         */
16826         "beforechildrenrendered":true
16827     });
16828
16829     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
16830
16831     /**
16832      * Read-only. The UI for this node
16833      * @type TreeNodeUI
16834      */
16835     this.ui = new uiClass(this);
16836     
16837     // finally support items[]
16838     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
16839         return;
16840     }
16841     
16842     
16843     Roo.each(this.attributes.items, function(c) {
16844         this.appendChild(Roo.factory(c,Roo.Tree));
16845     }, this);
16846     delete this.attributes.items;
16847     
16848     
16849     
16850 };
16851 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
16852     preventHScroll: true,
16853     /**
16854      * Returns true if this node is expanded
16855      * @return {Boolean}
16856      */
16857     isExpanded : function(){
16858         return this.expanded;
16859     },
16860
16861     /**
16862      * Returns the UI object for this node
16863      * @return {TreeNodeUI}
16864      */
16865     getUI : function(){
16866         return this.ui;
16867     },
16868
16869     // private override
16870     setFirstChild : function(node){
16871         var of = this.firstChild;
16872         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
16873         if(this.childrenRendered && of && node != of){
16874             of.renderIndent(true, true);
16875         }
16876         if(this.rendered){
16877             this.renderIndent(true, true);
16878         }
16879     },
16880
16881     // private override
16882     setLastChild : function(node){
16883         var ol = this.lastChild;
16884         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
16885         if(this.childrenRendered && ol && node != ol){
16886             ol.renderIndent(true, true);
16887         }
16888         if(this.rendered){
16889             this.renderIndent(true, true);
16890         }
16891     },
16892
16893     // these methods are overridden to provide lazy rendering support
16894     // private override
16895     appendChild : function()
16896     {
16897         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
16898         if(node && this.childrenRendered){
16899             node.render();
16900         }
16901         this.ui.updateExpandIcon();
16902         return node;
16903     },
16904
16905     // private override
16906     removeChild : function(node){
16907         this.ownerTree.getSelectionModel().unselect(node);
16908         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
16909         // if it's been rendered remove dom node
16910         if(this.childrenRendered){
16911             node.ui.remove();
16912         }
16913         if(this.childNodes.length < 1){
16914             this.collapse(false, false);
16915         }else{
16916             this.ui.updateExpandIcon();
16917         }
16918         if(!this.firstChild) {
16919             this.childrenRendered = false;
16920         }
16921         return node;
16922     },
16923
16924     // private override
16925     insertBefore : function(node, refNode){
16926         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
16927         if(newNode && refNode && this.childrenRendered){
16928             node.render();
16929         }
16930         this.ui.updateExpandIcon();
16931         return newNode;
16932     },
16933
16934     /**
16935      * Sets the text for this node
16936      * @param {String} text
16937      */
16938     setText : function(text){
16939         var oldText = this.text;
16940         this.text = text;
16941         this.attributes.text = text;
16942         if(this.rendered){ // event without subscribing
16943             this.ui.onTextChange(this, text, oldText);
16944         }
16945         this.fireEvent("textchange", this, text, oldText);
16946     },
16947
16948     /**
16949      * Triggers selection of this node
16950      */
16951     select : function(){
16952         this.getOwnerTree().getSelectionModel().select(this);
16953     },
16954
16955     /**
16956      * Triggers deselection of this node
16957      */
16958     unselect : function(){
16959         this.getOwnerTree().getSelectionModel().unselect(this);
16960     },
16961
16962     /**
16963      * Returns true if this node is selected
16964      * @return {Boolean}
16965      */
16966     isSelected : function(){
16967         return this.getOwnerTree().getSelectionModel().isSelected(this);
16968     },
16969
16970     /**
16971      * Expand this node.
16972      * @param {Boolean} deep (optional) True to expand all children as well
16973      * @param {Boolean} anim (optional) false to cancel the default animation
16974      * @param {Function} callback (optional) A callback to be called when
16975      * expanding this node completes (does not wait for deep expand to complete).
16976      * Called with 1 parameter, this node.
16977      */
16978     expand : function(deep, anim, callback){
16979         if(!this.expanded){
16980             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
16981                 return;
16982             }
16983             if(!this.childrenRendered){
16984                 this.renderChildren();
16985             }
16986             this.expanded = true;
16987             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
16988                 this.ui.animExpand(function(){
16989                     this.fireEvent("expand", this);
16990                     if(typeof callback == "function"){
16991                         callback(this);
16992                     }
16993                     if(deep === true){
16994                         this.expandChildNodes(true);
16995                     }
16996                 }.createDelegate(this));
16997                 return;
16998             }else{
16999                 this.ui.expand();
17000                 this.fireEvent("expand", this);
17001                 if(typeof callback == "function"){
17002                     callback(this);
17003                 }
17004             }
17005         }else{
17006            if(typeof callback == "function"){
17007                callback(this);
17008            }
17009         }
17010         if(deep === true){
17011             this.expandChildNodes(true);
17012         }
17013     },
17014
17015     isHiddenRoot : function(){
17016         return this.isRoot && !this.getOwnerTree().rootVisible;
17017     },
17018
17019     /**
17020      * Collapse this node.
17021      * @param {Boolean} deep (optional) True to collapse all children as well
17022      * @param {Boolean} anim (optional) false to cancel the default animation
17023      */
17024     collapse : function(deep, anim){
17025         if(this.expanded && !this.isHiddenRoot()){
17026             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17027                 return;
17028             }
17029             this.expanded = false;
17030             if((this.getOwnerTree().animate && anim !== false) || anim){
17031                 this.ui.animCollapse(function(){
17032                     this.fireEvent("collapse", this);
17033                     if(deep === true){
17034                         this.collapseChildNodes(true);
17035                     }
17036                 }.createDelegate(this));
17037                 return;
17038             }else{
17039                 this.ui.collapse();
17040                 this.fireEvent("collapse", this);
17041             }
17042         }
17043         if(deep === true){
17044             var cs = this.childNodes;
17045             for(var i = 0, len = cs.length; i < len; i++) {
17046                 cs[i].collapse(true, false);
17047             }
17048         }
17049     },
17050
17051     // private
17052     delayedExpand : function(delay){
17053         if(!this.expandProcId){
17054             this.expandProcId = this.expand.defer(delay, this);
17055         }
17056     },
17057
17058     // private
17059     cancelExpand : function(){
17060         if(this.expandProcId){
17061             clearTimeout(this.expandProcId);
17062         }
17063         this.expandProcId = false;
17064     },
17065
17066     /**
17067      * Toggles expanded/collapsed state of the node
17068      */
17069     toggle : function(){
17070         if(this.expanded){
17071             this.collapse();
17072         }else{
17073             this.expand();
17074         }
17075     },
17076
17077     /**
17078      * Ensures all parent nodes are expanded
17079      */
17080     ensureVisible : function(callback){
17081         var tree = this.getOwnerTree();
17082         tree.expandPath(this.parentNode.getPath(), false, function(){
17083             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17084             Roo.callback(callback);
17085         }.createDelegate(this));
17086     },
17087
17088     /**
17089      * Expand all child nodes
17090      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17091      */
17092     expandChildNodes : function(deep){
17093         var cs = this.childNodes;
17094         for(var i = 0, len = cs.length; i < len; i++) {
17095                 cs[i].expand(deep);
17096         }
17097     },
17098
17099     /**
17100      * Collapse all child nodes
17101      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17102      */
17103     collapseChildNodes : function(deep){
17104         var cs = this.childNodes;
17105         for(var i = 0, len = cs.length; i < len; i++) {
17106                 cs[i].collapse(deep);
17107         }
17108     },
17109
17110     /**
17111      * Disables this node
17112      */
17113     disable : function(){
17114         this.disabled = true;
17115         this.unselect();
17116         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17117             this.ui.onDisableChange(this, true);
17118         }
17119         this.fireEvent("disabledchange", this, true);
17120     },
17121
17122     /**
17123      * Enables this node
17124      */
17125     enable : function(){
17126         this.disabled = false;
17127         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17128             this.ui.onDisableChange(this, false);
17129         }
17130         this.fireEvent("disabledchange", this, false);
17131     },
17132
17133     // private
17134     renderChildren : function(suppressEvent){
17135         if(suppressEvent !== false){
17136             this.fireEvent("beforechildrenrendered", this);
17137         }
17138         var cs = this.childNodes;
17139         for(var i = 0, len = cs.length; i < len; i++){
17140             cs[i].render(true);
17141         }
17142         this.childrenRendered = true;
17143     },
17144
17145     // private
17146     sort : function(fn, scope){
17147         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17148         if(this.childrenRendered){
17149             var cs = this.childNodes;
17150             for(var i = 0, len = cs.length; i < len; i++){
17151                 cs[i].render(true);
17152             }
17153         }
17154     },
17155
17156     // private
17157     render : function(bulkRender){
17158         this.ui.render(bulkRender);
17159         if(!this.rendered){
17160             this.rendered = true;
17161             if(this.expanded){
17162                 this.expanded = false;
17163                 this.expand(false, false);
17164             }
17165         }
17166     },
17167
17168     // private
17169     renderIndent : function(deep, refresh){
17170         if(refresh){
17171             this.ui.childIndent = null;
17172         }
17173         this.ui.renderIndent();
17174         if(deep === true && this.childrenRendered){
17175             var cs = this.childNodes;
17176             for(var i = 0, len = cs.length; i < len; i++){
17177                 cs[i].renderIndent(true, refresh);
17178             }
17179         }
17180     }
17181 });/*
17182  * Based on:
17183  * Ext JS Library 1.1.1
17184  * Copyright(c) 2006-2007, Ext JS, LLC.
17185  *
17186  * Originally Released Under LGPL - original licence link has changed is not relivant.
17187  *
17188  * Fork - LGPL
17189  * <script type="text/javascript">
17190  */
17191  
17192 /**
17193  * @class Roo.tree.AsyncTreeNode
17194  * @extends Roo.tree.TreeNode
17195  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17196  * @constructor
17197  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17198  */
17199  Roo.tree.AsyncTreeNode = function(config){
17200     this.loaded = false;
17201     this.loading = false;
17202     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17203     /**
17204     * @event beforeload
17205     * Fires before this node is loaded, return false to cancel
17206     * @param {Node} this This node
17207     */
17208     this.addEvents({'beforeload':true, 'load': true});
17209     /**
17210     * @event load
17211     * Fires when this node is loaded
17212     * @param {Node} this This node
17213     */
17214     /**
17215      * The loader used by this node (defaults to using the tree's defined loader)
17216      * @type TreeLoader
17217      * @property loader
17218      */
17219 };
17220 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17221     expand : function(deep, anim, callback){
17222         if(this.loading){ // if an async load is already running, waiting til it's done
17223             var timer;
17224             var f = function(){
17225                 if(!this.loading){ // done loading
17226                     clearInterval(timer);
17227                     this.expand(deep, anim, callback);
17228                 }
17229             }.createDelegate(this);
17230             timer = setInterval(f, 200);
17231             return;
17232         }
17233         if(!this.loaded){
17234             if(this.fireEvent("beforeload", this) === false){
17235                 return;
17236             }
17237             this.loading = true;
17238             this.ui.beforeLoad(this);
17239             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17240             if(loader){
17241                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17242                 return;
17243             }
17244         }
17245         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17246     },
17247     
17248     /**
17249      * Returns true if this node is currently loading
17250      * @return {Boolean}
17251      */
17252     isLoading : function(){
17253         return this.loading;  
17254     },
17255     
17256     loadComplete : function(deep, anim, callback){
17257         this.loading = false;
17258         this.loaded = true;
17259         this.ui.afterLoad(this);
17260         this.fireEvent("load", this);
17261         this.expand(deep, anim, callback);
17262     },
17263     
17264     /**
17265      * Returns true if this node has been loaded
17266      * @return {Boolean}
17267      */
17268     isLoaded : function(){
17269         return this.loaded;
17270     },
17271     
17272     hasChildNodes : function(){
17273         if(!this.isLeaf() && !this.loaded){
17274             return true;
17275         }else{
17276             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17277         }
17278     },
17279
17280     /**
17281      * Trigger a reload for this node
17282      * @param {Function} callback
17283      */
17284     reload : function(callback){
17285         this.collapse(false, false);
17286         while(this.firstChild){
17287             this.removeChild(this.firstChild);
17288         }
17289         this.childrenRendered = false;
17290         this.loaded = false;
17291         if(this.isHiddenRoot()){
17292             this.expanded = false;
17293         }
17294         this.expand(false, false, callback);
17295     }
17296 });/*
17297  * Based on:
17298  * Ext JS Library 1.1.1
17299  * Copyright(c) 2006-2007, Ext JS, LLC.
17300  *
17301  * Originally Released Under LGPL - original licence link has changed is not relivant.
17302  *
17303  * Fork - LGPL
17304  * <script type="text/javascript">
17305  */
17306  
17307 /**
17308  * @class Roo.tree.TreeNodeUI
17309  * @constructor
17310  * @param {Object} node The node to render
17311  * The TreeNode UI implementation is separate from the
17312  * tree implementation. Unless you are customizing the tree UI,
17313  * you should never have to use this directly.
17314  */
17315 Roo.tree.TreeNodeUI = function(node){
17316     this.node = node;
17317     this.rendered = false;
17318     this.animating = false;
17319     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17320 };
17321
17322 Roo.tree.TreeNodeUI.prototype = {
17323     removeChild : function(node){
17324         if(this.rendered){
17325             this.ctNode.removeChild(node.ui.getEl());
17326         }
17327     },
17328
17329     beforeLoad : function(){
17330          this.addClass("x-tree-node-loading");
17331     },
17332
17333     afterLoad : function(){
17334          this.removeClass("x-tree-node-loading");
17335     },
17336
17337     onTextChange : function(node, text, oldText){
17338         if(this.rendered){
17339             this.textNode.innerHTML = text;
17340         }
17341     },
17342
17343     onDisableChange : function(node, state){
17344         this.disabled = state;
17345         if(state){
17346             this.addClass("x-tree-node-disabled");
17347         }else{
17348             this.removeClass("x-tree-node-disabled");
17349         }
17350     },
17351
17352     onSelectedChange : function(state){
17353         if(state){
17354             this.focus();
17355             this.addClass("x-tree-selected");
17356         }else{
17357             //this.blur();
17358             this.removeClass("x-tree-selected");
17359         }
17360     },
17361
17362     onMove : function(tree, node, oldParent, newParent, index, refNode){
17363         this.childIndent = null;
17364         if(this.rendered){
17365             var targetNode = newParent.ui.getContainer();
17366             if(!targetNode){//target not rendered
17367                 this.holder = document.createElement("div");
17368                 this.holder.appendChild(this.wrap);
17369                 return;
17370             }
17371             var insertBefore = refNode ? refNode.ui.getEl() : null;
17372             if(insertBefore){
17373                 targetNode.insertBefore(this.wrap, insertBefore);
17374             }else{
17375                 targetNode.appendChild(this.wrap);
17376             }
17377             this.node.renderIndent(true);
17378         }
17379     },
17380
17381     addClass : function(cls){
17382         if(this.elNode){
17383             Roo.fly(this.elNode).addClass(cls);
17384         }
17385     },
17386
17387     removeClass : function(cls){
17388         if(this.elNode){
17389             Roo.fly(this.elNode).removeClass(cls);
17390         }
17391     },
17392
17393     remove : function(){
17394         if(this.rendered){
17395             this.holder = document.createElement("div");
17396             this.holder.appendChild(this.wrap);
17397         }
17398     },
17399
17400     fireEvent : function(){
17401         return this.node.fireEvent.apply(this.node, arguments);
17402     },
17403
17404     initEvents : function(){
17405         this.node.on("move", this.onMove, this);
17406         var E = Roo.EventManager;
17407         var a = this.anchor;
17408
17409         var el = Roo.fly(a, '_treeui');
17410
17411         if(Roo.isOpera){ // opera render bug ignores the CSS
17412             el.setStyle("text-decoration", "none");
17413         }
17414
17415         el.on("click", this.onClick, this);
17416         el.on("dblclick", this.onDblClick, this);
17417
17418         if(this.checkbox){
17419             Roo.EventManager.on(this.checkbox,
17420                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17421         }
17422
17423         el.on("contextmenu", this.onContextMenu, this);
17424
17425         var icon = Roo.fly(this.iconNode);
17426         icon.on("click", this.onClick, this);
17427         icon.on("dblclick", this.onDblClick, this);
17428         icon.on("contextmenu", this.onContextMenu, this);
17429         E.on(this.ecNode, "click", this.ecClick, this, true);
17430
17431         if(this.node.disabled){
17432             this.addClass("x-tree-node-disabled");
17433         }
17434         if(this.node.hidden){
17435             this.addClass("x-tree-node-disabled");
17436         }
17437         var ot = this.node.getOwnerTree();
17438         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17439         if(dd && (!this.node.isRoot || ot.rootVisible)){
17440             Roo.dd.Registry.register(this.elNode, {
17441                 node: this.node,
17442                 handles: this.getDDHandles(),
17443                 isHandle: false
17444             });
17445         }
17446     },
17447
17448     getDDHandles : function(){
17449         return [this.iconNode, this.textNode];
17450     },
17451
17452     hide : function(){
17453         if(this.rendered){
17454             this.wrap.style.display = "none";
17455         }
17456     },
17457
17458     show : function(){
17459         if(this.rendered){
17460             this.wrap.style.display = "";
17461         }
17462     },
17463
17464     onContextMenu : function(e){
17465         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17466             e.preventDefault();
17467             this.focus();
17468             this.fireEvent("contextmenu", this.node, e);
17469         }
17470     },
17471
17472     onClick : function(e){
17473         if(this.dropping){
17474             e.stopEvent();
17475             return;
17476         }
17477         if(this.fireEvent("beforeclick", this.node, e) !== false){
17478             if(!this.disabled && this.node.attributes.href){
17479                 this.fireEvent("click", this.node, e);
17480                 return;
17481             }
17482             e.preventDefault();
17483             if(this.disabled){
17484                 return;
17485             }
17486
17487             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17488                 this.node.toggle();
17489             }
17490
17491             this.fireEvent("click", this.node, e);
17492         }else{
17493             e.stopEvent();
17494         }
17495     },
17496
17497     onDblClick : function(e){
17498         e.preventDefault();
17499         if(this.disabled){
17500             return;
17501         }
17502         if(this.checkbox){
17503             this.toggleCheck();
17504         }
17505         if(!this.animating && this.node.hasChildNodes()){
17506             this.node.toggle();
17507         }
17508         this.fireEvent("dblclick", this.node, e);
17509     },
17510
17511     onCheckChange : function(){
17512         var checked = this.checkbox.checked;
17513         this.node.attributes.checked = checked;
17514         this.fireEvent('checkchange', this.node, checked);
17515     },
17516
17517     ecClick : function(e){
17518         if(!this.animating && this.node.hasChildNodes()){
17519             this.node.toggle();
17520         }
17521     },
17522
17523     startDrop : function(){
17524         this.dropping = true;
17525     },
17526
17527     // delayed drop so the click event doesn't get fired on a drop
17528     endDrop : function(){
17529        setTimeout(function(){
17530            this.dropping = false;
17531        }.createDelegate(this), 50);
17532     },
17533
17534     expand : function(){
17535         this.updateExpandIcon();
17536         this.ctNode.style.display = "";
17537     },
17538
17539     focus : function(){
17540         if(!this.node.preventHScroll){
17541             try{this.anchor.focus();
17542             }catch(e){}
17543         }else if(!Roo.isIE){
17544             try{
17545                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17546                 var l = noscroll.scrollLeft;
17547                 this.anchor.focus();
17548                 noscroll.scrollLeft = l;
17549             }catch(e){}
17550         }
17551     },
17552
17553     toggleCheck : function(value){
17554         var cb = this.checkbox;
17555         if(cb){
17556             cb.checked = (value === undefined ? !cb.checked : value);
17557         }
17558     },
17559
17560     blur : function(){
17561         try{
17562             this.anchor.blur();
17563         }catch(e){}
17564     },
17565
17566     animExpand : function(callback){
17567         var ct = Roo.get(this.ctNode);
17568         ct.stopFx();
17569         if(!this.node.hasChildNodes()){
17570             this.updateExpandIcon();
17571             this.ctNode.style.display = "";
17572             Roo.callback(callback);
17573             return;
17574         }
17575         this.animating = true;
17576         this.updateExpandIcon();
17577
17578         ct.slideIn('t', {
17579            callback : function(){
17580                this.animating = false;
17581                Roo.callback(callback);
17582             },
17583             scope: this,
17584             duration: this.node.ownerTree.duration || .25
17585         });
17586     },
17587
17588     highlight : function(){
17589         var tree = this.node.getOwnerTree();
17590         Roo.fly(this.wrap).highlight(
17591             tree.hlColor || "C3DAF9",
17592             {endColor: tree.hlBaseColor}
17593         );
17594     },
17595
17596     collapse : function(){
17597         this.updateExpandIcon();
17598         this.ctNode.style.display = "none";
17599     },
17600
17601     animCollapse : function(callback){
17602         var ct = Roo.get(this.ctNode);
17603         ct.enableDisplayMode('block');
17604         ct.stopFx();
17605
17606         this.animating = true;
17607         this.updateExpandIcon();
17608
17609         ct.slideOut('t', {
17610             callback : function(){
17611                this.animating = false;
17612                Roo.callback(callback);
17613             },
17614             scope: this,
17615             duration: this.node.ownerTree.duration || .25
17616         });
17617     },
17618
17619     getContainer : function(){
17620         return this.ctNode;
17621     },
17622
17623     getEl : function(){
17624         return this.wrap;
17625     },
17626
17627     appendDDGhost : function(ghostNode){
17628         ghostNode.appendChild(this.elNode.cloneNode(true));
17629     },
17630
17631     getDDRepairXY : function(){
17632         return Roo.lib.Dom.getXY(this.iconNode);
17633     },
17634
17635     onRender : function(){
17636         this.render();
17637     },
17638
17639     render : function(bulkRender){
17640         var n = this.node, a = n.attributes;
17641         var targetNode = n.parentNode ?
17642               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
17643
17644         if(!this.rendered){
17645             this.rendered = true;
17646
17647             this.renderElements(n, a, targetNode, bulkRender);
17648
17649             if(a.qtip){
17650                if(this.textNode.setAttributeNS){
17651                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
17652                    if(a.qtipTitle){
17653                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
17654                    }
17655                }else{
17656                    this.textNode.setAttribute("ext:qtip", a.qtip);
17657                    if(a.qtipTitle){
17658                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
17659                    }
17660                }
17661             }else if(a.qtipCfg){
17662                 a.qtipCfg.target = Roo.id(this.textNode);
17663                 Roo.QuickTips.register(a.qtipCfg);
17664             }
17665             this.initEvents();
17666             if(!this.node.expanded){
17667                 this.updateExpandIcon();
17668             }
17669         }else{
17670             if(bulkRender === true) {
17671                 targetNode.appendChild(this.wrap);
17672             }
17673         }
17674     },
17675
17676     renderElements : function(n, a, targetNode, bulkRender)
17677     {
17678         // add some indent caching, this helps performance when rendering a large tree
17679         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
17680         var t = n.getOwnerTree();
17681         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
17682         if (typeof(n.attributes.html) != 'undefined') {
17683             txt = n.attributes.html;
17684         }
17685         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
17686         var cb = typeof a.checked == 'boolean';
17687         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
17688         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
17689             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
17690             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
17691             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
17692             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
17693             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
17694              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
17695                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
17696             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
17697             "</li>"];
17698
17699         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
17700             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
17701                                 n.nextSibling.ui.getEl(), buf.join(""));
17702         }else{
17703             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
17704         }
17705
17706         this.elNode = this.wrap.childNodes[0];
17707         this.ctNode = this.wrap.childNodes[1];
17708         var cs = this.elNode.childNodes;
17709         this.indentNode = cs[0];
17710         this.ecNode = cs[1];
17711         this.iconNode = cs[2];
17712         var index = 3;
17713         if(cb){
17714             this.checkbox = cs[3];
17715             index++;
17716         }
17717         this.anchor = cs[index];
17718         this.textNode = cs[index].firstChild;
17719     },
17720
17721     getAnchor : function(){
17722         return this.anchor;
17723     },
17724
17725     getTextEl : function(){
17726         return this.textNode;
17727     },
17728
17729     getIconEl : function(){
17730         return this.iconNode;
17731     },
17732
17733     isChecked : function(){
17734         return this.checkbox ? this.checkbox.checked : false;
17735     },
17736
17737     updateExpandIcon : function(){
17738         if(this.rendered){
17739             var n = this.node, c1, c2;
17740             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
17741             var hasChild = n.hasChildNodes();
17742             if(hasChild){
17743                 if(n.expanded){
17744                     cls += "-minus";
17745                     c1 = "x-tree-node-collapsed";
17746                     c2 = "x-tree-node-expanded";
17747                 }else{
17748                     cls += "-plus";
17749                     c1 = "x-tree-node-expanded";
17750                     c2 = "x-tree-node-collapsed";
17751                 }
17752                 if(this.wasLeaf){
17753                     this.removeClass("x-tree-node-leaf");
17754                     this.wasLeaf = false;
17755                 }
17756                 if(this.c1 != c1 || this.c2 != c2){
17757                     Roo.fly(this.elNode).replaceClass(c1, c2);
17758                     this.c1 = c1; this.c2 = c2;
17759                 }
17760             }else{
17761                 // this changes non-leafs into leafs if they have no children.
17762                 // it's not very rational behaviour..
17763                 
17764                 if(!this.wasLeaf && this.node.leaf){
17765                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
17766                     delete this.c1;
17767                     delete this.c2;
17768                     this.wasLeaf = true;
17769                 }
17770             }
17771             var ecc = "x-tree-ec-icon "+cls;
17772             if(this.ecc != ecc){
17773                 this.ecNode.className = ecc;
17774                 this.ecc = ecc;
17775             }
17776         }
17777     },
17778
17779     getChildIndent : function(){
17780         if(!this.childIndent){
17781             var buf = [];
17782             var p = this.node;
17783             while(p){
17784                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
17785                     if(!p.isLast()) {
17786                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
17787                     } else {
17788                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
17789                     }
17790                 }
17791                 p = p.parentNode;
17792             }
17793             this.childIndent = buf.join("");
17794         }
17795         return this.childIndent;
17796     },
17797
17798     renderIndent : function(){
17799         if(this.rendered){
17800             var indent = "";
17801             var p = this.node.parentNode;
17802             if(p){
17803                 indent = p.ui.getChildIndent();
17804             }
17805             if(this.indentMarkup != indent){ // don't rerender if not required
17806                 this.indentNode.innerHTML = indent;
17807                 this.indentMarkup = indent;
17808             }
17809             this.updateExpandIcon();
17810         }
17811     }
17812 };
17813
17814 Roo.tree.RootTreeNodeUI = function(){
17815     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
17816 };
17817 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
17818     render : function(){
17819         if(!this.rendered){
17820             var targetNode = this.node.ownerTree.innerCt.dom;
17821             this.node.expanded = true;
17822             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
17823             this.wrap = this.ctNode = targetNode.firstChild;
17824         }
17825     },
17826     collapse : function(){
17827     },
17828     expand : function(){
17829     }
17830 });/*
17831  * Based on:
17832  * Ext JS Library 1.1.1
17833  * Copyright(c) 2006-2007, Ext JS, LLC.
17834  *
17835  * Originally Released Under LGPL - original licence link has changed is not relivant.
17836  *
17837  * Fork - LGPL
17838  * <script type="text/javascript">
17839  */
17840 /**
17841  * @class Roo.tree.TreeLoader
17842  * @extends Roo.util.Observable
17843  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
17844  * nodes from a specified URL. The response must be a javascript Array definition
17845  * who's elements are node definition objects. eg:
17846  * <pre><code>
17847 {  success : true,
17848    data :      [
17849    
17850     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
17851     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
17852     ]
17853 }
17854
17855
17856 </code></pre>
17857  * <br><br>
17858  * The old style respose with just an array is still supported, but not recommended.
17859  * <br><br>
17860  *
17861  * A server request is sent, and child nodes are loaded only when a node is expanded.
17862  * The loading node's id is passed to the server under the parameter name "node" to
17863  * enable the server to produce the correct child nodes.
17864  * <br><br>
17865  * To pass extra parameters, an event handler may be attached to the "beforeload"
17866  * event, and the parameters specified in the TreeLoader's baseParams property:
17867  * <pre><code>
17868     myTreeLoader.on("beforeload", function(treeLoader, node) {
17869         this.baseParams.category = node.attributes.category;
17870     }, this);
17871 </code></pre><
17872  * This would pass an HTTP parameter called "category" to the server containing
17873  * the value of the Node's "category" attribute.
17874  * @constructor
17875  * Creates a new Treeloader.
17876  * @param {Object} config A config object containing config properties.
17877  */
17878 Roo.tree.TreeLoader = function(config){
17879     this.baseParams = {};
17880     this.requestMethod = "POST";
17881     Roo.apply(this, config);
17882
17883     this.addEvents({
17884     
17885         /**
17886          * @event beforeload
17887          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
17888          * @param {Object} This TreeLoader object.
17889          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17890          * @param {Object} callback The callback function specified in the {@link #load} call.
17891          */
17892         beforeload : true,
17893         /**
17894          * @event load
17895          * Fires when the node has been successfuly loaded.
17896          * @param {Object} This TreeLoader object.
17897          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17898          * @param {Object} response The response object containing the data from the server.
17899          */
17900         load : true,
17901         /**
17902          * @event loadexception
17903          * Fires if the network request failed.
17904          * @param {Object} This TreeLoader object.
17905          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17906          * @param {Object} response The response object containing the data from the server.
17907          */
17908         loadexception : true,
17909         /**
17910          * @event create
17911          * Fires before a node is created, enabling you to return custom Node types 
17912          * @param {Object} This TreeLoader object.
17913          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
17914          */
17915         create : true
17916     });
17917
17918     Roo.tree.TreeLoader.superclass.constructor.call(this);
17919 };
17920
17921 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
17922     /**
17923     * @cfg {String} dataUrl The URL from which to request a Json string which
17924     * specifies an array of node definition object representing the child nodes
17925     * to be loaded.
17926     */
17927     /**
17928     * @cfg {String} requestMethod either GET or POST
17929     * defaults to POST (due to BC)
17930     * to be loaded.
17931     */
17932     /**
17933     * @cfg {Object} baseParams (optional) An object containing properties which
17934     * specify HTTP parameters to be passed to each request for child nodes.
17935     */
17936     /**
17937     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
17938     * created by this loader. If the attributes sent by the server have an attribute in this object,
17939     * they take priority.
17940     */
17941     /**
17942     * @cfg {Object} uiProviders (optional) An object containing properties which
17943     * 
17944     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
17945     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
17946     * <i>uiProvider</i> attribute of a returned child node is a string rather
17947     * than a reference to a TreeNodeUI implementation, this that string value
17948     * is used as a property name in the uiProviders object. You can define the provider named
17949     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
17950     */
17951     uiProviders : {},
17952
17953     /**
17954     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
17955     * child nodes before loading.
17956     */
17957     clearOnLoad : true,
17958
17959     /**
17960     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
17961     * property on loading, rather than expecting an array. (eg. more compatible to a standard
17962     * Grid query { data : [ .....] }
17963     */
17964     
17965     root : false,
17966      /**
17967     * @cfg {String} queryParam (optional) 
17968     * Name of the query as it will be passed on the querystring (defaults to 'node')
17969     * eg. the request will be ?node=[id]
17970     */
17971     
17972     
17973     queryParam: false,
17974     
17975     /**
17976      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
17977      * This is called automatically when a node is expanded, but may be used to reload
17978      * a node (or append new children if the {@link #clearOnLoad} option is false.)
17979      * @param {Roo.tree.TreeNode} node
17980      * @param {Function} callback
17981      */
17982     load : function(node, callback){
17983         if(this.clearOnLoad){
17984             while(node.firstChild){
17985                 node.removeChild(node.firstChild);
17986             }
17987         }
17988         if(node.attributes.children){ // preloaded json children
17989             var cs = node.attributes.children;
17990             for(var i = 0, len = cs.length; i < len; i++){
17991                 node.appendChild(this.createNode(cs[i]));
17992             }
17993             if(typeof callback == "function"){
17994                 callback();
17995             }
17996         }else if(this.dataUrl){
17997             this.requestData(node, callback);
17998         }
17999     },
18000
18001     getParams: function(node){
18002         var buf = [], bp = this.baseParams;
18003         for(var key in bp){
18004             if(typeof bp[key] != "function"){
18005                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18006             }
18007         }
18008         var n = this.queryParam === false ? 'node' : this.queryParam;
18009         buf.push(n + "=", encodeURIComponent(node.id));
18010         return buf.join("");
18011     },
18012
18013     requestData : function(node, callback){
18014         if(this.fireEvent("beforeload", this, node, callback) !== false){
18015             this.transId = Roo.Ajax.request({
18016                 method:this.requestMethod,
18017                 url: this.dataUrl||this.url,
18018                 success: this.handleResponse,
18019                 failure: this.handleFailure,
18020                 scope: this,
18021                 argument: {callback: callback, node: node},
18022                 params: this.getParams(node)
18023             });
18024         }else{
18025             // if the load is cancelled, make sure we notify
18026             // the node that we are done
18027             if(typeof callback == "function"){
18028                 callback();
18029             }
18030         }
18031     },
18032
18033     isLoading : function(){
18034         return this.transId ? true : false;
18035     },
18036
18037     abort : function(){
18038         if(this.isLoading()){
18039             Roo.Ajax.abort(this.transId);
18040         }
18041     },
18042
18043     // private
18044     createNode : function(attr)
18045     {
18046         // apply baseAttrs, nice idea Corey!
18047         if(this.baseAttrs){
18048             Roo.applyIf(attr, this.baseAttrs);
18049         }
18050         if(this.applyLoader !== false){
18051             attr.loader = this;
18052         }
18053         // uiProvider = depreciated..
18054         
18055         if(typeof(attr.uiProvider) == 'string'){
18056            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18057                 /**  eval:var:attr */ eval(attr.uiProvider);
18058         }
18059         if(typeof(this.uiProviders['default']) != 'undefined') {
18060             attr.uiProvider = this.uiProviders['default'];
18061         }
18062         
18063         this.fireEvent('create', this, attr);
18064         
18065         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18066         return(attr.leaf ?
18067                         new Roo.tree.TreeNode(attr) :
18068                         new Roo.tree.AsyncTreeNode(attr));
18069     },
18070
18071     processResponse : function(response, node, callback)
18072     {
18073         var json = response.responseText;
18074         try {
18075             
18076             var o = Roo.decode(json);
18077             
18078             if (this.root === false && typeof(o.success) != undefined) {
18079                 this.root = 'data'; // the default behaviour for list like data..
18080                 }
18081                 
18082             if (this.root !== false &&  !o.success) {
18083                 // it's a failure condition.
18084                 var a = response.argument;
18085                 this.fireEvent("loadexception", this, a.node, response);
18086                 Roo.log("Load failed - should have a handler really");
18087                 return;
18088             }
18089             
18090             
18091             
18092             if (this.root !== false) {
18093                  o = o[this.root];
18094             }
18095             
18096             for(var i = 0, len = o.length; i < len; i++){
18097                 var n = this.createNode(o[i]);
18098                 if(n){
18099                     node.appendChild(n);
18100                 }
18101             }
18102             if(typeof callback == "function"){
18103                 callback(this, node);
18104             }
18105         }catch(e){
18106             this.handleFailure(response);
18107         }
18108     },
18109
18110     handleResponse : function(response){
18111         this.transId = false;
18112         var a = response.argument;
18113         this.processResponse(response, a.node, a.callback);
18114         this.fireEvent("load", this, a.node, response);
18115     },
18116
18117     handleFailure : function(response)
18118     {
18119         // should handle failure better..
18120         this.transId = false;
18121         var a = response.argument;
18122         this.fireEvent("loadexception", this, a.node, response);
18123         if(typeof a.callback == "function"){
18124             a.callback(this, a.node);
18125         }
18126     }
18127 });/*
18128  * Based on:
18129  * Ext JS Library 1.1.1
18130  * Copyright(c) 2006-2007, Ext JS, LLC.
18131  *
18132  * Originally Released Under LGPL - original licence link has changed is not relivant.
18133  *
18134  * Fork - LGPL
18135  * <script type="text/javascript">
18136  */
18137
18138 /**
18139 * @class Roo.tree.TreeFilter
18140 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18141 * @param {TreePanel} tree
18142 * @param {Object} config (optional)
18143  */
18144 Roo.tree.TreeFilter = function(tree, config){
18145     this.tree = tree;
18146     this.filtered = {};
18147     Roo.apply(this, config);
18148 };
18149
18150 Roo.tree.TreeFilter.prototype = {
18151     clearBlank:false,
18152     reverse:false,
18153     autoClear:false,
18154     remove:false,
18155
18156      /**
18157      * Filter the data by a specific attribute.
18158      * @param {String/RegExp} value Either string that the attribute value
18159      * should start with or a RegExp to test against the attribute
18160      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18161      * @param {TreeNode} startNode (optional) The node to start the filter at.
18162      */
18163     filter : function(value, attr, startNode){
18164         attr = attr || "text";
18165         var f;
18166         if(typeof value == "string"){
18167             var vlen = value.length;
18168             // auto clear empty filter
18169             if(vlen == 0 && this.clearBlank){
18170                 this.clear();
18171                 return;
18172             }
18173             value = value.toLowerCase();
18174             f = function(n){
18175                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18176             };
18177         }else if(value.exec){ // regex?
18178             f = function(n){
18179                 return value.test(n.attributes[attr]);
18180             };
18181         }else{
18182             throw 'Illegal filter type, must be string or regex';
18183         }
18184         this.filterBy(f, null, startNode);
18185         },
18186
18187     /**
18188      * Filter by a function. The passed function will be called with each
18189      * node in the tree (or from the startNode). If the function returns true, the node is kept
18190      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18191      * @param {Function} fn The filter function
18192      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18193      */
18194     filterBy : function(fn, scope, startNode){
18195         startNode = startNode || this.tree.root;
18196         if(this.autoClear){
18197             this.clear();
18198         }
18199         var af = this.filtered, rv = this.reverse;
18200         var f = function(n){
18201             if(n == startNode){
18202                 return true;
18203             }
18204             if(af[n.id]){
18205                 return false;
18206             }
18207             var m = fn.call(scope || n, n);
18208             if(!m || rv){
18209                 af[n.id] = n;
18210                 n.ui.hide();
18211                 return false;
18212             }
18213             return true;
18214         };
18215         startNode.cascade(f);
18216         if(this.remove){
18217            for(var id in af){
18218                if(typeof id != "function"){
18219                    var n = af[id];
18220                    if(n && n.parentNode){
18221                        n.parentNode.removeChild(n);
18222                    }
18223                }
18224            }
18225         }
18226     },
18227
18228     /**
18229      * Clears the current filter. Note: with the "remove" option
18230      * set a filter cannot be cleared.
18231      */
18232     clear : function(){
18233         var t = this.tree;
18234         var af = this.filtered;
18235         for(var id in af){
18236             if(typeof id != "function"){
18237                 var n = af[id];
18238                 if(n){
18239                     n.ui.show();
18240                 }
18241             }
18242         }
18243         this.filtered = {};
18244     }
18245 };
18246 /*
18247  * Based on:
18248  * Ext JS Library 1.1.1
18249  * Copyright(c) 2006-2007, Ext JS, LLC.
18250  *
18251  * Originally Released Under LGPL - original licence link has changed is not relivant.
18252  *
18253  * Fork - LGPL
18254  * <script type="text/javascript">
18255  */
18256  
18257
18258 /**
18259  * @class Roo.tree.TreeSorter
18260  * Provides sorting of nodes in a TreePanel
18261  * 
18262  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18263  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18264  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18265  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18266  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18267  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18268  * @constructor
18269  * @param {TreePanel} tree
18270  * @param {Object} config
18271  */
18272 Roo.tree.TreeSorter = function(tree, config){
18273     Roo.apply(this, config);
18274     tree.on("beforechildrenrendered", this.doSort, this);
18275     tree.on("append", this.updateSort, this);
18276     tree.on("insert", this.updateSort, this);
18277     
18278     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18279     var p = this.property || "text";
18280     var sortType = this.sortType;
18281     var fs = this.folderSort;
18282     var cs = this.caseSensitive === true;
18283     var leafAttr = this.leafAttr || 'leaf';
18284
18285     this.sortFn = function(n1, n2){
18286         if(fs){
18287             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18288                 return 1;
18289             }
18290             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18291                 return -1;
18292             }
18293         }
18294         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18295         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18296         if(v1 < v2){
18297                         return dsc ? +1 : -1;
18298                 }else if(v1 > v2){
18299                         return dsc ? -1 : +1;
18300         }else{
18301                 return 0;
18302         }
18303     };
18304 };
18305
18306 Roo.tree.TreeSorter.prototype = {
18307     doSort : function(node){
18308         node.sort(this.sortFn);
18309     },
18310     
18311     compareNodes : function(n1, n2){
18312         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18313     },
18314     
18315     updateSort : function(tree, node){
18316         if(node.childrenRendered){
18317             this.doSort.defer(1, this, [node]);
18318         }
18319     }
18320 };/*
18321  * Based on:
18322  * Ext JS Library 1.1.1
18323  * Copyright(c) 2006-2007, Ext JS, LLC.
18324  *
18325  * Originally Released Under LGPL - original licence link has changed is not relivant.
18326  *
18327  * Fork - LGPL
18328  * <script type="text/javascript">
18329  */
18330
18331 if(Roo.dd.DropZone){
18332     
18333 Roo.tree.TreeDropZone = function(tree, config){
18334     this.allowParentInsert = false;
18335     this.allowContainerDrop = false;
18336     this.appendOnly = false;
18337     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18338     this.tree = tree;
18339     this.lastInsertClass = "x-tree-no-status";
18340     this.dragOverData = {};
18341 };
18342
18343 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18344     ddGroup : "TreeDD",
18345     scroll:  true,
18346     
18347     expandDelay : 1000,
18348     
18349     expandNode : function(node){
18350         if(node.hasChildNodes() && !node.isExpanded()){
18351             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18352         }
18353     },
18354     
18355     queueExpand : function(node){
18356         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18357     },
18358     
18359     cancelExpand : function(){
18360         if(this.expandProcId){
18361             clearTimeout(this.expandProcId);
18362             this.expandProcId = false;
18363         }
18364     },
18365     
18366     isValidDropPoint : function(n, pt, dd, e, data){
18367         if(!n || !data){ return false; }
18368         var targetNode = n.node;
18369         var dropNode = data.node;
18370         // default drop rules
18371         if(!(targetNode && targetNode.isTarget && pt)){
18372             return false;
18373         }
18374         if(pt == "append" && targetNode.allowChildren === false){
18375             return false;
18376         }
18377         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18378             return false;
18379         }
18380         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18381             return false;
18382         }
18383         // reuse the object
18384         var overEvent = this.dragOverData;
18385         overEvent.tree = this.tree;
18386         overEvent.target = targetNode;
18387         overEvent.data = data;
18388         overEvent.point = pt;
18389         overEvent.source = dd;
18390         overEvent.rawEvent = e;
18391         overEvent.dropNode = dropNode;
18392         overEvent.cancel = false;  
18393         var result = this.tree.fireEvent("nodedragover", overEvent);
18394         return overEvent.cancel === false && result !== false;
18395     },
18396     
18397     getDropPoint : function(e, n, dd)
18398     {
18399         var tn = n.node;
18400         if(tn.isRoot){
18401             return tn.allowChildren !== false ? "append" : false; // always append for root
18402         }
18403         var dragEl = n.ddel;
18404         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18405         var y = Roo.lib.Event.getPageY(e);
18406         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18407         
18408         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18409         var noAppend = tn.allowChildren === false;
18410         if(this.appendOnly || tn.parentNode.allowChildren === false){
18411             return noAppend ? false : "append";
18412         }
18413         var noBelow = false;
18414         if(!this.allowParentInsert){
18415             noBelow = tn.hasChildNodes() && tn.isExpanded();
18416         }
18417         var q = (b - t) / (noAppend ? 2 : 3);
18418         if(y >= t && y < (t + q)){
18419             return "above";
18420         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18421             return "below";
18422         }else{
18423             return "append";
18424         }
18425     },
18426     
18427     onNodeEnter : function(n, dd, e, data)
18428     {
18429         this.cancelExpand();
18430     },
18431     
18432     onNodeOver : function(n, dd, e, data)
18433     {
18434        
18435         var pt = this.getDropPoint(e, n, dd);
18436         var node = n.node;
18437         
18438         // auto node expand check
18439         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18440             this.queueExpand(node);
18441         }else if(pt != "append"){
18442             this.cancelExpand();
18443         }
18444         
18445         // set the insert point style on the target node
18446         var returnCls = this.dropNotAllowed;
18447         if(this.isValidDropPoint(n, pt, dd, e, data)){
18448            if(pt){
18449                var el = n.ddel;
18450                var cls;
18451                if(pt == "above"){
18452                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18453                    cls = "x-tree-drag-insert-above";
18454                }else if(pt == "below"){
18455                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18456                    cls = "x-tree-drag-insert-below";
18457                }else{
18458                    returnCls = "x-tree-drop-ok-append";
18459                    cls = "x-tree-drag-append";
18460                }
18461                if(this.lastInsertClass != cls){
18462                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18463                    this.lastInsertClass = cls;
18464                }
18465            }
18466        }
18467        return returnCls;
18468     },
18469     
18470     onNodeOut : function(n, dd, e, data){
18471         
18472         this.cancelExpand();
18473         this.removeDropIndicators(n);
18474     },
18475     
18476     onNodeDrop : function(n, dd, e, data){
18477         var point = this.getDropPoint(e, n, dd);
18478         var targetNode = n.node;
18479         targetNode.ui.startDrop();
18480         if(!this.isValidDropPoint(n, point, dd, e, data)){
18481             targetNode.ui.endDrop();
18482             return false;
18483         }
18484         // first try to find the drop node
18485         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18486         var dropEvent = {
18487             tree : this.tree,
18488             target: targetNode,
18489             data: data,
18490             point: point,
18491             source: dd,
18492             rawEvent: e,
18493             dropNode: dropNode,
18494             cancel: !dropNode   
18495         };
18496         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18497         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18498             targetNode.ui.endDrop();
18499             return false;
18500         }
18501         // allow target changing
18502         targetNode = dropEvent.target;
18503         if(point == "append" && !targetNode.isExpanded()){
18504             targetNode.expand(false, null, function(){
18505                 this.completeDrop(dropEvent);
18506             }.createDelegate(this));
18507         }else{
18508             this.completeDrop(dropEvent);
18509         }
18510         return true;
18511     },
18512     
18513     completeDrop : function(de){
18514         var ns = de.dropNode, p = de.point, t = de.target;
18515         if(!(ns instanceof Array)){
18516             ns = [ns];
18517         }
18518         var n;
18519         for(var i = 0, len = ns.length; i < len; i++){
18520             n = ns[i];
18521             if(p == "above"){
18522                 t.parentNode.insertBefore(n, t);
18523             }else if(p == "below"){
18524                 t.parentNode.insertBefore(n, t.nextSibling);
18525             }else{
18526                 t.appendChild(n);
18527             }
18528         }
18529         n.ui.focus();
18530         if(this.tree.hlDrop){
18531             n.ui.highlight();
18532         }
18533         t.ui.endDrop();
18534         this.tree.fireEvent("nodedrop", de);
18535     },
18536     
18537     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18538         if(this.tree.hlDrop){
18539             dropNode.ui.focus();
18540             dropNode.ui.highlight();
18541         }
18542         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18543     },
18544     
18545     getTree : function(){
18546         return this.tree;
18547     },
18548     
18549     removeDropIndicators : function(n){
18550         if(n && n.ddel){
18551             var el = n.ddel;
18552             Roo.fly(el).removeClass([
18553                     "x-tree-drag-insert-above",
18554                     "x-tree-drag-insert-below",
18555                     "x-tree-drag-append"]);
18556             this.lastInsertClass = "_noclass";
18557         }
18558     },
18559     
18560     beforeDragDrop : function(target, e, id){
18561         this.cancelExpand();
18562         return true;
18563     },
18564     
18565     afterRepair : function(data){
18566         if(data && Roo.enableFx){
18567             data.node.ui.highlight();
18568         }
18569         this.hideProxy();
18570     } 
18571     
18572 });
18573
18574 }
18575 /*
18576  * Based on:
18577  * Ext JS Library 1.1.1
18578  * Copyright(c) 2006-2007, Ext JS, LLC.
18579  *
18580  * Originally Released Under LGPL - original licence link has changed is not relivant.
18581  *
18582  * Fork - LGPL
18583  * <script type="text/javascript">
18584  */
18585  
18586
18587 if(Roo.dd.DragZone){
18588 Roo.tree.TreeDragZone = function(tree, config){
18589     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18590     this.tree = tree;
18591 };
18592
18593 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18594     ddGroup : "TreeDD",
18595    
18596     onBeforeDrag : function(data, e){
18597         var n = data.node;
18598         return n && n.draggable && !n.disabled;
18599     },
18600      
18601     
18602     onInitDrag : function(e){
18603         var data = this.dragData;
18604         this.tree.getSelectionModel().select(data.node);
18605         this.proxy.update("");
18606         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18607         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18608     },
18609     
18610     getRepairXY : function(e, data){
18611         return data.node.ui.getDDRepairXY();
18612     },
18613     
18614     onEndDrag : function(data, e){
18615         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18616         
18617         
18618     },
18619     
18620     onValidDrop : function(dd, e, id){
18621         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18622         this.hideProxy();
18623     },
18624     
18625     beforeInvalidDrop : function(e, id){
18626         // this scrolls the original position back into view
18627         var sm = this.tree.getSelectionModel();
18628         sm.clearSelections();
18629         sm.select(this.dragData.node);
18630     }
18631 });
18632 }/*
18633  * Based on:
18634  * Ext JS Library 1.1.1
18635  * Copyright(c) 2006-2007, Ext JS, LLC.
18636  *
18637  * Originally Released Under LGPL - original licence link has changed is not relivant.
18638  *
18639  * Fork - LGPL
18640  * <script type="text/javascript">
18641  */
18642 /**
18643  * @class Roo.tree.TreeEditor
18644  * @extends Roo.Editor
18645  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
18646  * as the editor field.
18647  * @constructor
18648  * @param {Object} config (used to be the tree panel.)
18649  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
18650  * 
18651  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
18652  * @cfg {Roo.form.TextField|Object} field The field configuration
18653  *
18654  * 
18655  */
18656 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
18657     var tree = config;
18658     var field;
18659     if (oldconfig) { // old style..
18660         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
18661     } else {
18662         // new style..
18663         tree = config.tree;
18664         config.field = config.field  || {};
18665         config.field.xtype = 'TextField';
18666         field = Roo.factory(config.field, Roo.form);
18667     }
18668     config = config || {};
18669     
18670     
18671     this.addEvents({
18672         /**
18673          * @event beforenodeedit
18674          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
18675          * false from the handler of this event.
18676          * @param {Editor} this
18677          * @param {Roo.tree.Node} node 
18678          */
18679         "beforenodeedit" : true
18680     });
18681     
18682     //Roo.log(config);
18683     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
18684
18685     this.tree = tree;
18686
18687     tree.on('beforeclick', this.beforeNodeClick, this);
18688     tree.getTreeEl().on('mousedown', this.hide, this);
18689     this.on('complete', this.updateNode, this);
18690     this.on('beforestartedit', this.fitToTree, this);
18691     this.on('startedit', this.bindScroll, this, {delay:10});
18692     this.on('specialkey', this.onSpecialKey, this);
18693 };
18694
18695 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
18696     /**
18697      * @cfg {String} alignment
18698      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
18699      */
18700     alignment: "l-l",
18701     // inherit
18702     autoSize: false,
18703     /**
18704      * @cfg {Boolean} hideEl
18705      * True to hide the bound element while the editor is displayed (defaults to false)
18706      */
18707     hideEl : false,
18708     /**
18709      * @cfg {String} cls
18710      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
18711      */
18712     cls: "x-small-editor x-tree-editor",
18713     /**
18714      * @cfg {Boolean} shim
18715      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
18716      */
18717     shim:false,
18718     // inherit
18719     shadow:"frame",
18720     /**
18721      * @cfg {Number} maxWidth
18722      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
18723      * the containing tree element's size, it will be automatically limited for you to the container width, taking
18724      * scroll and client offsets into account prior to each edit.
18725      */
18726     maxWidth: 250,
18727
18728     editDelay : 350,
18729
18730     // private
18731     fitToTree : function(ed, el){
18732         var td = this.tree.getTreeEl().dom, nd = el.dom;
18733         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
18734             td.scrollLeft = nd.offsetLeft;
18735         }
18736         var w = Math.min(
18737                 this.maxWidth,
18738                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
18739         this.setSize(w, '');
18740         
18741         return this.fireEvent('beforenodeedit', this, this.editNode);
18742         
18743     },
18744
18745     // private
18746     triggerEdit : function(node){
18747         this.completeEdit();
18748         this.editNode = node;
18749         this.startEdit(node.ui.textNode, node.text);
18750     },
18751
18752     // private
18753     bindScroll : function(){
18754         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
18755     },
18756
18757     // private
18758     beforeNodeClick : function(node, e){
18759         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
18760         this.lastClick = new Date();
18761         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
18762             e.stopEvent();
18763             this.triggerEdit(node);
18764             return false;
18765         }
18766         return true;
18767     },
18768
18769     // private
18770     updateNode : function(ed, value){
18771         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
18772         this.editNode.setText(value);
18773     },
18774
18775     // private
18776     onHide : function(){
18777         Roo.tree.TreeEditor.superclass.onHide.call(this);
18778         if(this.editNode){
18779             this.editNode.ui.focus();
18780         }
18781     },
18782
18783     // private
18784     onSpecialKey : function(field, e){
18785         var k = e.getKey();
18786         if(k == e.ESC){
18787             e.stopEvent();
18788             this.cancelEdit();
18789         }else if(k == e.ENTER && !e.hasModifier()){
18790             e.stopEvent();
18791             this.completeEdit();
18792         }
18793     }
18794 });//<Script type="text/javascript">
18795 /*
18796  * Based on:
18797  * Ext JS Library 1.1.1
18798  * Copyright(c) 2006-2007, Ext JS, LLC.
18799  *
18800  * Originally Released Under LGPL - original licence link has changed is not relivant.
18801  *
18802  * Fork - LGPL
18803  * <script type="text/javascript">
18804  */
18805  
18806 /**
18807  * Not documented??? - probably should be...
18808  */
18809
18810 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
18811     //focus: Roo.emptyFn, // prevent odd scrolling behavior
18812     
18813     renderElements : function(n, a, targetNode, bulkRender){
18814         //consel.log("renderElements?");
18815         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18816
18817         var t = n.getOwnerTree();
18818         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
18819         
18820         var cols = t.columns;
18821         var bw = t.borderWidth;
18822         var c = cols[0];
18823         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18824          var cb = typeof a.checked == "boolean";
18825         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18826         var colcls = 'x-t-' + tid + '-c0';
18827         var buf = [
18828             '<li class="x-tree-node">',
18829             
18830                 
18831                 '<div class="x-tree-node-el ', a.cls,'">',
18832                     // extran...
18833                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
18834                 
18835                 
18836                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
18837                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
18838                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
18839                            (a.icon ? ' x-tree-node-inline-icon' : ''),
18840                            (a.iconCls ? ' '+a.iconCls : ''),
18841                            '" unselectable="on" />',
18842                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
18843                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
18844                              
18845                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18846                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
18847                             '<span unselectable="on" qtip="' + tx + '">',
18848                              tx,
18849                              '</span></a>' ,
18850                     '</div>',
18851                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18852                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
18853                  ];
18854         for(var i = 1, len = cols.length; i < len; i++){
18855             c = cols[i];
18856             colcls = 'x-t-' + tid + '-c' +i;
18857             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18858             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
18859                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
18860                       "</div>");
18861          }
18862          
18863          buf.push(
18864             '</a>',
18865             '<div class="x-clear"></div></div>',
18866             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18867             "</li>");
18868         
18869         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18870             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18871                                 n.nextSibling.ui.getEl(), buf.join(""));
18872         }else{
18873             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18874         }
18875         var el = this.wrap.firstChild;
18876         this.elRow = el;
18877         this.elNode = el.firstChild;
18878         this.ranchor = el.childNodes[1];
18879         this.ctNode = this.wrap.childNodes[1];
18880         var cs = el.firstChild.childNodes;
18881         this.indentNode = cs[0];
18882         this.ecNode = cs[1];
18883         this.iconNode = cs[2];
18884         var index = 3;
18885         if(cb){
18886             this.checkbox = cs[3];
18887             index++;
18888         }
18889         this.anchor = cs[index];
18890         
18891         this.textNode = cs[index].firstChild;
18892         
18893         //el.on("click", this.onClick, this);
18894         //el.on("dblclick", this.onDblClick, this);
18895         
18896         
18897        // console.log(this);
18898     },
18899     initEvents : function(){
18900         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
18901         
18902             
18903         var a = this.ranchor;
18904
18905         var el = Roo.get(a);
18906
18907         if(Roo.isOpera){ // opera render bug ignores the CSS
18908             el.setStyle("text-decoration", "none");
18909         }
18910
18911         el.on("click", this.onClick, this);
18912         el.on("dblclick", this.onDblClick, this);
18913         el.on("contextmenu", this.onContextMenu, this);
18914         
18915     },
18916     
18917     /*onSelectedChange : function(state){
18918         if(state){
18919             this.focus();
18920             this.addClass("x-tree-selected");
18921         }else{
18922             //this.blur();
18923             this.removeClass("x-tree-selected");
18924         }
18925     },*/
18926     addClass : function(cls){
18927         if(this.elRow){
18928             Roo.fly(this.elRow).addClass(cls);
18929         }
18930         
18931     },
18932     
18933     
18934     removeClass : function(cls){
18935         if(this.elRow){
18936             Roo.fly(this.elRow).removeClass(cls);
18937         }
18938     }
18939
18940     
18941     
18942 });//<Script type="text/javascript">
18943
18944 /*
18945  * Based on:
18946  * Ext JS Library 1.1.1
18947  * Copyright(c) 2006-2007, Ext JS, LLC.
18948  *
18949  * Originally Released Under LGPL - original licence link has changed is not relivant.
18950  *
18951  * Fork - LGPL
18952  * <script type="text/javascript">
18953  */
18954  
18955
18956 /**
18957  * @class Roo.tree.ColumnTree
18958  * @extends Roo.data.TreePanel
18959  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
18960  * @cfg {int} borderWidth  compined right/left border allowance
18961  * @constructor
18962  * @param {String/HTMLElement/Element} el The container element
18963  * @param {Object} config
18964  */
18965 Roo.tree.ColumnTree =  function(el, config)
18966 {
18967    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
18968    this.addEvents({
18969         /**
18970         * @event resize
18971         * Fire this event on a container when it resizes
18972         * @param {int} w Width
18973         * @param {int} h Height
18974         */
18975        "resize" : true
18976     });
18977     this.on('resize', this.onResize, this);
18978 };
18979
18980 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
18981     //lines:false,
18982     
18983     
18984     borderWidth: Roo.isBorderBox ? 0 : 2, 
18985     headEls : false,
18986     
18987     render : function(){
18988         // add the header.....
18989        
18990         Roo.tree.ColumnTree.superclass.render.apply(this);
18991         
18992         this.el.addClass('x-column-tree');
18993         
18994         this.headers = this.el.createChild(
18995             {cls:'x-tree-headers'},this.innerCt.dom);
18996    
18997         var cols = this.columns, c;
18998         var totalWidth = 0;
18999         this.headEls = [];
19000         var  len = cols.length;
19001         for(var i = 0; i < len; i++){
19002              c = cols[i];
19003              totalWidth += c.width;
19004             this.headEls.push(this.headers.createChild({
19005                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19006                  cn: {
19007                      cls:'x-tree-hd-text',
19008                      html: c.header
19009                  },
19010                  style:'width:'+(c.width-this.borderWidth)+'px;'
19011              }));
19012         }
19013         this.headers.createChild({cls:'x-clear'});
19014         // prevent floats from wrapping when clipped
19015         this.headers.setWidth(totalWidth);
19016         //this.innerCt.setWidth(totalWidth);
19017         this.innerCt.setStyle({ overflow: 'auto' });
19018         this.onResize(this.width, this.height);
19019              
19020         
19021     },
19022     onResize : function(w,h)
19023     {
19024         this.height = h;
19025         this.width = w;
19026         // resize cols..
19027         this.innerCt.setWidth(this.width);
19028         this.innerCt.setHeight(this.height-20);
19029         
19030         // headers...
19031         var cols = this.columns, c;
19032         var totalWidth = 0;
19033         var expEl = false;
19034         var len = cols.length;
19035         for(var i = 0; i < len; i++){
19036             c = cols[i];
19037             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19038                 // it's the expander..
19039                 expEl  = this.headEls[i];
19040                 continue;
19041             }
19042             totalWidth += c.width;
19043             
19044         }
19045         if (expEl) {
19046             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19047         }
19048         this.headers.setWidth(w-20);
19049
19050         
19051         
19052         
19053     }
19054 });
19055 /*
19056  * Based on:
19057  * Ext JS Library 1.1.1
19058  * Copyright(c) 2006-2007, Ext JS, LLC.
19059  *
19060  * Originally Released Under LGPL - original licence link has changed is not relivant.
19061  *
19062  * Fork - LGPL
19063  * <script type="text/javascript">
19064  */
19065  
19066 /**
19067  * @class Roo.menu.Menu
19068  * @extends Roo.util.Observable
19069  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19070  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19071  * @constructor
19072  * Creates a new Menu
19073  * @param {Object} config Configuration options
19074  */
19075 Roo.menu.Menu = function(config){
19076     Roo.apply(this, config);
19077     this.id = this.id || Roo.id();
19078     this.addEvents({
19079         /**
19080          * @event beforeshow
19081          * Fires before this menu is displayed
19082          * @param {Roo.menu.Menu} this
19083          */
19084         beforeshow : true,
19085         /**
19086          * @event beforehide
19087          * Fires before this menu is hidden
19088          * @param {Roo.menu.Menu} this
19089          */
19090         beforehide : true,
19091         /**
19092          * @event show
19093          * Fires after this menu is displayed
19094          * @param {Roo.menu.Menu} this
19095          */
19096         show : true,
19097         /**
19098          * @event hide
19099          * Fires after this menu is hidden
19100          * @param {Roo.menu.Menu} this
19101          */
19102         hide : true,
19103         /**
19104          * @event click
19105          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19106          * @param {Roo.menu.Menu} this
19107          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19108          * @param {Roo.EventObject} e
19109          */
19110         click : true,
19111         /**
19112          * @event mouseover
19113          * Fires when the mouse is hovering over this menu
19114          * @param {Roo.menu.Menu} this
19115          * @param {Roo.EventObject} e
19116          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19117          */
19118         mouseover : true,
19119         /**
19120          * @event mouseout
19121          * Fires when the mouse exits this menu
19122          * @param {Roo.menu.Menu} this
19123          * @param {Roo.EventObject} e
19124          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19125          */
19126         mouseout : true,
19127         /**
19128          * @event itemclick
19129          * Fires when a menu item contained in this menu is clicked
19130          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19131          * @param {Roo.EventObject} e
19132          */
19133         itemclick: true
19134     });
19135     if (this.registerMenu) {
19136         Roo.menu.MenuMgr.register(this);
19137     }
19138     
19139     var mis = this.items;
19140     this.items = new Roo.util.MixedCollection();
19141     if(mis){
19142         this.add.apply(this, mis);
19143     }
19144 };
19145
19146 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19147     /**
19148      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19149      */
19150     minWidth : 120,
19151     /**
19152      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19153      * for bottom-right shadow (defaults to "sides")
19154      */
19155     shadow : "sides",
19156     /**
19157      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19158      * this menu (defaults to "tl-tr?")
19159      */
19160     subMenuAlign : "tl-tr?",
19161     /**
19162      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19163      * relative to its element of origin (defaults to "tl-bl?")
19164      */
19165     defaultAlign : "tl-bl?",
19166     /**
19167      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19168      */
19169     allowOtherMenus : false,
19170     /**
19171      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19172      */
19173     registerMenu : true,
19174
19175     hidden:true,
19176
19177     // private
19178     render : function(){
19179         if(this.el){
19180             return;
19181         }
19182         var el = this.el = new Roo.Layer({
19183             cls: "x-menu",
19184             shadow:this.shadow,
19185             constrain: false,
19186             parentEl: this.parentEl || document.body,
19187             zindex:15000
19188         });
19189
19190         this.keyNav = new Roo.menu.MenuNav(this);
19191
19192         if(this.plain){
19193             el.addClass("x-menu-plain");
19194         }
19195         if(this.cls){
19196             el.addClass(this.cls);
19197         }
19198         // generic focus element
19199         this.focusEl = el.createChild({
19200             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19201         });
19202         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19203         ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
19204         
19205         ul.on("mouseover", this.onMouseOver, this);
19206         ul.on("mouseout", this.onMouseOut, this);
19207         this.items.each(function(item){
19208             if (item.hidden) {
19209                 return;
19210             }
19211             
19212             var li = document.createElement("li");
19213             li.className = "x-menu-list-item";
19214             ul.dom.appendChild(li);
19215             item.render(li, this);
19216         }, this);
19217         this.ul = ul;
19218         this.autoWidth();
19219     },
19220
19221     // private
19222     autoWidth : function(){
19223         var el = this.el, ul = this.ul;
19224         if(!el){
19225             return;
19226         }
19227         var w = this.width;
19228         if(w){
19229             el.setWidth(w);
19230         }else if(Roo.isIE){
19231             el.setWidth(this.minWidth);
19232             var t = el.dom.offsetWidth; // force recalc
19233             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19234         }
19235     },
19236
19237     // private
19238     delayAutoWidth : function(){
19239         if(this.rendered){
19240             if(!this.awTask){
19241                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19242             }
19243             this.awTask.delay(20);
19244         }
19245     },
19246
19247     // private
19248     findTargetItem : function(e){
19249         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19250         if(t && t.menuItemId){
19251             return this.items.get(t.menuItemId);
19252         }
19253     },
19254
19255     // private
19256     onClick : function(e){
19257         Roo.log("menu.onClick");
19258         var t = this.findTargetItem(e);
19259         if(!t){
19260             return;
19261         }
19262         Roo.log(e);
19263         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
19264             if(t == this.activeItem && t.shouldDeactivate(e)){
19265                 this.activeItem.deactivate();
19266                 delete this.activeItem;
19267                 return;
19268             }
19269             if(t.canActivate){
19270                 this.setActiveItem(t, true);
19271             }
19272             return;
19273             
19274             
19275         }
19276         
19277         t.onClick(e);
19278         this.fireEvent("click", this, t, e);
19279     },
19280
19281     // private
19282     setActiveItem : function(item, autoExpand){
19283         if(item != this.activeItem){
19284             if(this.activeItem){
19285                 this.activeItem.deactivate();
19286             }
19287             this.activeItem = item;
19288             item.activate(autoExpand);
19289         }else if(autoExpand){
19290             item.expandMenu();
19291         }
19292     },
19293
19294     // private
19295     tryActivate : function(start, step){
19296         var items = this.items;
19297         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19298             var item = items.get(i);
19299             if(!item.disabled && item.canActivate){
19300                 this.setActiveItem(item, false);
19301                 return item;
19302             }
19303         }
19304         return false;
19305     },
19306
19307     // private
19308     onMouseOver : function(e){
19309         var t;
19310         if(t = this.findTargetItem(e)){
19311             if(t.canActivate && !t.disabled){
19312                 this.setActiveItem(t, true);
19313             }
19314         }
19315         this.fireEvent("mouseover", this, e, t);
19316     },
19317
19318     // private
19319     onMouseOut : function(e){
19320         var t;
19321         if(t = this.findTargetItem(e)){
19322             if(t == this.activeItem && t.shouldDeactivate(e)){
19323                 this.activeItem.deactivate();
19324                 delete this.activeItem;
19325             }
19326         }
19327         this.fireEvent("mouseout", this, e, t);
19328     },
19329
19330     /**
19331      * Read-only.  Returns true if the menu is currently displayed, else false.
19332      * @type Boolean
19333      */
19334     isVisible : function(){
19335         return this.el && !this.hidden;
19336     },
19337
19338     /**
19339      * Displays this menu relative to another element
19340      * @param {String/HTMLElement/Roo.Element} element The element to align to
19341      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19342      * the element (defaults to this.defaultAlign)
19343      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19344      */
19345     show : function(el, pos, parentMenu){
19346         this.parentMenu = parentMenu;
19347         if(!this.el){
19348             this.render();
19349         }
19350         this.fireEvent("beforeshow", this);
19351         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19352     },
19353
19354     /**
19355      * Displays this menu at a specific xy position
19356      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19357      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19358      */
19359     showAt : function(xy, parentMenu, /* private: */_e){
19360         this.parentMenu = parentMenu;
19361         if(!this.el){
19362             this.render();
19363         }
19364         if(_e !== false){
19365             this.fireEvent("beforeshow", this);
19366             xy = this.el.adjustForConstraints(xy);
19367         }
19368         this.el.setXY(xy);
19369         this.el.show();
19370         this.hidden = false;
19371         this.focus();
19372         this.fireEvent("show", this);
19373     },
19374
19375     focus : function(){
19376         if(!this.hidden){
19377             this.doFocus.defer(50, this);
19378         }
19379     },
19380
19381     doFocus : function(){
19382         if(!this.hidden){
19383             this.focusEl.focus();
19384         }
19385     },
19386
19387     /**
19388      * Hides this menu and optionally all parent menus
19389      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19390      */
19391     hide : function(deep){
19392         if(this.el && this.isVisible()){
19393             this.fireEvent("beforehide", this);
19394             if(this.activeItem){
19395                 this.activeItem.deactivate();
19396                 this.activeItem = null;
19397             }
19398             this.el.hide();
19399             this.hidden = true;
19400             this.fireEvent("hide", this);
19401         }
19402         if(deep === true && this.parentMenu){
19403             this.parentMenu.hide(true);
19404         }
19405     },
19406
19407     /**
19408      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19409      * Any of the following are valid:
19410      * <ul>
19411      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19412      * <li>An HTMLElement object which will be converted to a menu item</li>
19413      * <li>A menu item config object that will be created as a new menu item</li>
19414      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19415      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19416      * </ul>
19417      * Usage:
19418      * <pre><code>
19419 // Create the menu
19420 var menu = new Roo.menu.Menu();
19421
19422 // Create a menu item to add by reference
19423 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19424
19425 // Add a bunch of items at once using different methods.
19426 // Only the last item added will be returned.
19427 var item = menu.add(
19428     menuItem,                // add existing item by ref
19429     'Dynamic Item',          // new TextItem
19430     '-',                     // new separator
19431     { text: 'Config Item' }  // new item by config
19432 );
19433 </code></pre>
19434      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19435      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19436      */
19437     add : function(){
19438         var a = arguments, l = a.length, item;
19439         for(var i = 0; i < l; i++){
19440             var el = a[i];
19441             if ((typeof(el) == "object") && el.xtype && el.xns) {
19442                 el = Roo.factory(el, Roo.menu);
19443             }
19444             
19445             if(el.render){ // some kind of Item
19446                 item = this.addItem(el);
19447             }else if(typeof el == "string"){ // string
19448                 if(el == "separator" || el == "-"){
19449                     item = this.addSeparator();
19450                 }else{
19451                     item = this.addText(el);
19452                 }
19453             }else if(el.tagName || el.el){ // element
19454                 item = this.addElement(el);
19455             }else if(typeof el == "object"){ // must be menu item config?
19456                 item = this.addMenuItem(el);
19457             }
19458         }
19459         return item;
19460     },
19461
19462     /**
19463      * Returns this menu's underlying {@link Roo.Element} object
19464      * @return {Roo.Element} The element
19465      */
19466     getEl : function(){
19467         if(!this.el){
19468             this.render();
19469         }
19470         return this.el;
19471     },
19472
19473     /**
19474      * Adds a separator bar to the menu
19475      * @return {Roo.menu.Item} The menu item that was added
19476      */
19477     addSeparator : function(){
19478         return this.addItem(new Roo.menu.Separator());
19479     },
19480
19481     /**
19482      * Adds an {@link Roo.Element} object to the menu
19483      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19484      * @return {Roo.menu.Item} The menu item that was added
19485      */
19486     addElement : function(el){
19487         return this.addItem(new Roo.menu.BaseItem(el));
19488     },
19489
19490     /**
19491      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19492      * @param {Roo.menu.Item} item The menu item to add
19493      * @return {Roo.menu.Item} The menu item that was added
19494      */
19495     addItem : function(item){
19496         this.items.add(item);
19497         if(this.ul){
19498             var li = document.createElement("li");
19499             li.className = "x-menu-list-item";
19500             this.ul.dom.appendChild(li);
19501             item.render(li, this);
19502             this.delayAutoWidth();
19503         }
19504         return item;
19505     },
19506
19507     /**
19508      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19509      * @param {Object} config A MenuItem config object
19510      * @return {Roo.menu.Item} The menu item that was added
19511      */
19512     addMenuItem : function(config){
19513         if(!(config instanceof Roo.menu.Item)){
19514             if(typeof config.checked == "boolean"){ // must be check menu item config?
19515                 config = new Roo.menu.CheckItem(config);
19516             }else{
19517                 config = new Roo.menu.Item(config);
19518             }
19519         }
19520         return this.addItem(config);
19521     },
19522
19523     /**
19524      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19525      * @param {String} text The text to display in the menu item
19526      * @return {Roo.menu.Item} The menu item that was added
19527      */
19528     addText : function(text){
19529         return this.addItem(new Roo.menu.TextItem({ text : text }));
19530     },
19531
19532     /**
19533      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19534      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19535      * @param {Roo.menu.Item} item The menu item to add
19536      * @return {Roo.menu.Item} The menu item that was added
19537      */
19538     insert : function(index, item){
19539         this.items.insert(index, item);
19540         if(this.ul){
19541             var li = document.createElement("li");
19542             li.className = "x-menu-list-item";
19543             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19544             item.render(li, this);
19545             this.delayAutoWidth();
19546         }
19547         return item;
19548     },
19549
19550     /**
19551      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19552      * @param {Roo.menu.Item} item The menu item to remove
19553      */
19554     remove : function(item){
19555         this.items.removeKey(item.id);
19556         item.destroy();
19557     },
19558
19559     /**
19560      * Removes and destroys all items in the menu
19561      */
19562     removeAll : function(){
19563         var f;
19564         while(f = this.items.first()){
19565             this.remove(f);
19566         }
19567     }
19568 });
19569
19570 // MenuNav is a private utility class used internally by the Menu
19571 Roo.menu.MenuNav = function(menu){
19572     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19573     this.scope = this.menu = menu;
19574 };
19575
19576 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19577     doRelay : function(e, h){
19578         var k = e.getKey();
19579         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19580             this.menu.tryActivate(0, 1);
19581             return false;
19582         }
19583         return h.call(this.scope || this, e, this.menu);
19584     },
19585
19586     up : function(e, m){
19587         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19588             m.tryActivate(m.items.length-1, -1);
19589         }
19590     },
19591
19592     down : function(e, m){
19593         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19594             m.tryActivate(0, 1);
19595         }
19596     },
19597
19598     right : function(e, m){
19599         if(m.activeItem){
19600             m.activeItem.expandMenu(true);
19601         }
19602     },
19603
19604     left : function(e, m){
19605         m.hide();
19606         if(m.parentMenu && m.parentMenu.activeItem){
19607             m.parentMenu.activeItem.activate();
19608         }
19609     },
19610
19611     enter : function(e, m){
19612         if(m.activeItem){
19613             e.stopPropagation();
19614             m.activeItem.onClick(e);
19615             m.fireEvent("click", this, m.activeItem);
19616             return true;
19617         }
19618     }
19619 });/*
19620  * Based on:
19621  * Ext JS Library 1.1.1
19622  * Copyright(c) 2006-2007, Ext JS, LLC.
19623  *
19624  * Originally Released Under LGPL - original licence link has changed is not relivant.
19625  *
19626  * Fork - LGPL
19627  * <script type="text/javascript">
19628  */
19629  
19630 /**
19631  * @class Roo.menu.MenuMgr
19632  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19633  * @singleton
19634  */
19635 Roo.menu.MenuMgr = function(){
19636    var menus, active, groups = {}, attached = false, lastShow = new Date();
19637
19638    // private - called when first menu is created
19639    function init(){
19640        menus = {};
19641        active = new Roo.util.MixedCollection();
19642        Roo.get(document).addKeyListener(27, function(){
19643            if(active.length > 0){
19644                hideAll();
19645            }
19646        });
19647    }
19648
19649    // private
19650    function hideAll(){
19651        if(active && active.length > 0){
19652            var c = active.clone();
19653            c.each(function(m){
19654                m.hide();
19655            });
19656        }
19657    }
19658
19659    // private
19660    function onHide(m){
19661        active.remove(m);
19662        if(active.length < 1){
19663            Roo.get(document).un("mousedown", onMouseDown);
19664            attached = false;
19665        }
19666    }
19667
19668    // private
19669    function onShow(m){
19670        var last = active.last();
19671        lastShow = new Date();
19672        active.add(m);
19673        if(!attached){
19674            Roo.get(document).on("mousedown", onMouseDown);
19675            attached = true;
19676        }
19677        if(m.parentMenu){
19678           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19679           m.parentMenu.activeChild = m;
19680        }else if(last && last.isVisible()){
19681           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19682        }
19683    }
19684
19685    // private
19686    function onBeforeHide(m){
19687        if(m.activeChild){
19688            m.activeChild.hide();
19689        }
19690        if(m.autoHideTimer){
19691            clearTimeout(m.autoHideTimer);
19692            delete m.autoHideTimer;
19693        }
19694    }
19695
19696    // private
19697    function onBeforeShow(m){
19698        var pm = m.parentMenu;
19699        if(!pm && !m.allowOtherMenus){
19700            hideAll();
19701        }else if(pm && pm.activeChild && active != m){
19702            pm.activeChild.hide();
19703        }
19704    }
19705
19706    // private
19707    function onMouseDown(e){
19708        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
19709            hideAll();
19710        }
19711    }
19712
19713    // private
19714    function onBeforeCheck(mi, state){
19715        if(state){
19716            var g = groups[mi.group];
19717            for(var i = 0, l = g.length; i < l; i++){
19718                if(g[i] != mi){
19719                    g[i].setChecked(false);
19720                }
19721            }
19722        }
19723    }
19724
19725    return {
19726
19727        /**
19728         * Hides all menus that are currently visible
19729         */
19730        hideAll : function(){
19731             hideAll();  
19732        },
19733
19734        // private
19735        register : function(menu){
19736            if(!menus){
19737                init();
19738            }
19739            menus[menu.id] = menu;
19740            menu.on("beforehide", onBeforeHide);
19741            menu.on("hide", onHide);
19742            menu.on("beforeshow", onBeforeShow);
19743            menu.on("show", onShow);
19744            var g = menu.group;
19745            if(g && menu.events["checkchange"]){
19746                if(!groups[g]){
19747                    groups[g] = [];
19748                }
19749                groups[g].push(menu);
19750                menu.on("checkchange", onCheck);
19751            }
19752        },
19753
19754         /**
19755          * Returns a {@link Roo.menu.Menu} object
19756          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
19757          * be used to generate and return a new Menu instance.
19758          */
19759        get : function(menu){
19760            if(typeof menu == "string"){ // menu id
19761                return menus[menu];
19762            }else if(menu.events){  // menu instance
19763                return menu;
19764            }else if(typeof menu.length == 'number'){ // array of menu items?
19765                return new Roo.menu.Menu({items:menu});
19766            }else{ // otherwise, must be a config
19767                return new Roo.menu.Menu(menu);
19768            }
19769        },
19770
19771        // private
19772        unregister : function(menu){
19773            delete menus[menu.id];
19774            menu.un("beforehide", onBeforeHide);
19775            menu.un("hide", onHide);
19776            menu.un("beforeshow", onBeforeShow);
19777            menu.un("show", onShow);
19778            var g = menu.group;
19779            if(g && menu.events["checkchange"]){
19780                groups[g].remove(menu);
19781                menu.un("checkchange", onCheck);
19782            }
19783        },
19784
19785        // private
19786        registerCheckable : function(menuItem){
19787            var g = menuItem.group;
19788            if(g){
19789                if(!groups[g]){
19790                    groups[g] = [];
19791                }
19792                groups[g].push(menuItem);
19793                menuItem.on("beforecheckchange", onBeforeCheck);
19794            }
19795        },
19796
19797        // private
19798        unregisterCheckable : function(menuItem){
19799            var g = menuItem.group;
19800            if(g){
19801                groups[g].remove(menuItem);
19802                menuItem.un("beforecheckchange", onBeforeCheck);
19803            }
19804        }
19805    };
19806 }();/*
19807  * Based on:
19808  * Ext JS Library 1.1.1
19809  * Copyright(c) 2006-2007, Ext JS, LLC.
19810  *
19811  * Originally Released Under LGPL - original licence link has changed is not relivant.
19812  *
19813  * Fork - LGPL
19814  * <script type="text/javascript">
19815  */
19816  
19817
19818 /**
19819  * @class Roo.menu.BaseItem
19820  * @extends Roo.Component
19821  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
19822  * management and base configuration options shared by all menu components.
19823  * @constructor
19824  * Creates a new BaseItem
19825  * @param {Object} config Configuration options
19826  */
19827 Roo.menu.BaseItem = function(config){
19828     Roo.menu.BaseItem.superclass.constructor.call(this, config);
19829
19830     this.addEvents({
19831         /**
19832          * @event click
19833          * Fires when this item is clicked
19834          * @param {Roo.menu.BaseItem} this
19835          * @param {Roo.EventObject} e
19836          */
19837         click: true,
19838         /**
19839          * @event activate
19840          * Fires when this item is activated
19841          * @param {Roo.menu.BaseItem} this
19842          */
19843         activate : true,
19844         /**
19845          * @event deactivate
19846          * Fires when this item is deactivated
19847          * @param {Roo.menu.BaseItem} this
19848          */
19849         deactivate : true
19850     });
19851
19852     if(this.handler){
19853         this.on("click", this.handler, this.scope, true);
19854     }
19855 };
19856
19857 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
19858     /**
19859      * @cfg {Function} handler
19860      * A function that will handle the click event of this menu item (defaults to undefined)
19861      */
19862     /**
19863      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
19864      */
19865     canActivate : false,
19866     
19867      /**
19868      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
19869      */
19870     hidden: false,
19871     
19872     /**
19873      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
19874      */
19875     activeClass : "x-menu-item-active",
19876     /**
19877      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
19878      */
19879     hideOnClick : true,
19880     /**
19881      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
19882      */
19883     hideDelay : 100,
19884
19885     // private
19886     ctype: "Roo.menu.BaseItem",
19887
19888     // private
19889     actionMode : "container",
19890
19891     // private
19892     render : function(container, parentMenu){
19893         this.parentMenu = parentMenu;
19894         Roo.menu.BaseItem.superclass.render.call(this, container);
19895         this.container.menuItemId = this.id;
19896     },
19897
19898     // private
19899     onRender : function(container, position){
19900         this.el = Roo.get(this.el);
19901         container.dom.appendChild(this.el.dom);
19902     },
19903
19904     // private
19905     onClick : function(e){
19906         if(!this.disabled && this.fireEvent("click", this, e) !== false
19907                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
19908             this.handleClick(e);
19909         }else{
19910             e.stopEvent();
19911         }
19912     },
19913
19914     // private
19915     activate : function(){
19916         if(this.disabled){
19917             return false;
19918         }
19919         var li = this.container;
19920         li.addClass(this.activeClass);
19921         this.region = li.getRegion().adjust(2, 2, -2, -2);
19922         this.fireEvent("activate", this);
19923         return true;
19924     },
19925
19926     // private
19927     deactivate : function(){
19928         this.container.removeClass(this.activeClass);
19929         this.fireEvent("deactivate", this);
19930     },
19931
19932     // private
19933     shouldDeactivate : function(e){
19934         return !this.region || !this.region.contains(e.getPoint());
19935     },
19936
19937     // private
19938     handleClick : function(e){
19939         if(this.hideOnClick){
19940             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
19941         }
19942     },
19943
19944     // private
19945     expandMenu : function(autoActivate){
19946         // do nothing
19947     },
19948
19949     // private
19950     hideMenu : function(){
19951         // do nothing
19952     }
19953 });/*
19954  * Based on:
19955  * Ext JS Library 1.1.1
19956  * Copyright(c) 2006-2007, Ext JS, LLC.
19957  *
19958  * Originally Released Under LGPL - original licence link has changed is not relivant.
19959  *
19960  * Fork - LGPL
19961  * <script type="text/javascript">
19962  */
19963  
19964 /**
19965  * @class Roo.menu.Adapter
19966  * @extends Roo.menu.BaseItem
19967  * 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.
19968  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
19969  * @constructor
19970  * Creates a new Adapter
19971  * @param {Object} config Configuration options
19972  */
19973 Roo.menu.Adapter = function(component, config){
19974     Roo.menu.Adapter.superclass.constructor.call(this, config);
19975     this.component = component;
19976 };
19977 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
19978     // private
19979     canActivate : true,
19980
19981     // private
19982     onRender : function(container, position){
19983         this.component.render(container);
19984         this.el = this.component.getEl();
19985     },
19986
19987     // private
19988     activate : function(){
19989         if(this.disabled){
19990             return false;
19991         }
19992         this.component.focus();
19993         this.fireEvent("activate", this);
19994         return true;
19995     },
19996
19997     // private
19998     deactivate : function(){
19999         this.fireEvent("deactivate", this);
20000     },
20001
20002     // private
20003     disable : function(){
20004         this.component.disable();
20005         Roo.menu.Adapter.superclass.disable.call(this);
20006     },
20007
20008     // private
20009     enable : function(){
20010         this.component.enable();
20011         Roo.menu.Adapter.superclass.enable.call(this);
20012     }
20013 });/*
20014  * Based on:
20015  * Ext JS Library 1.1.1
20016  * Copyright(c) 2006-2007, Ext JS, LLC.
20017  *
20018  * Originally Released Under LGPL - original licence link has changed is not relivant.
20019  *
20020  * Fork - LGPL
20021  * <script type="text/javascript">
20022  */
20023
20024 /**
20025  * @class Roo.menu.TextItem
20026  * @extends Roo.menu.BaseItem
20027  * Adds a static text string to a menu, usually used as either a heading or group separator.
20028  * Note: old style constructor with text is still supported.
20029  * 
20030  * @constructor
20031  * Creates a new TextItem
20032  * @param {Object} cfg Configuration
20033  */
20034 Roo.menu.TextItem = function(cfg){
20035     if (typeof(cfg) == 'string') {
20036         this.text = cfg;
20037     } else {
20038         Roo.apply(this,cfg);
20039     }
20040     
20041     Roo.menu.TextItem.superclass.constructor.call(this);
20042 };
20043
20044 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20045     /**
20046      * @cfg {Boolean} text Text to show on item.
20047      */
20048     text : '',
20049     
20050     /**
20051      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20052      */
20053     hideOnClick : false,
20054     /**
20055      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20056      */
20057     itemCls : "x-menu-text",
20058
20059     // private
20060     onRender : function(){
20061         var s = document.createElement("span");
20062         s.className = this.itemCls;
20063         s.innerHTML = this.text;
20064         this.el = s;
20065         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20066     }
20067 });/*
20068  * Based on:
20069  * Ext JS Library 1.1.1
20070  * Copyright(c) 2006-2007, Ext JS, LLC.
20071  *
20072  * Originally Released Under LGPL - original licence link has changed is not relivant.
20073  *
20074  * Fork - LGPL
20075  * <script type="text/javascript">
20076  */
20077
20078 /**
20079  * @class Roo.menu.Separator
20080  * @extends Roo.menu.BaseItem
20081  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20082  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20083  * @constructor
20084  * @param {Object} config Configuration options
20085  */
20086 Roo.menu.Separator = function(config){
20087     Roo.menu.Separator.superclass.constructor.call(this, config);
20088 };
20089
20090 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20091     /**
20092      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20093      */
20094     itemCls : "x-menu-sep",
20095     /**
20096      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20097      */
20098     hideOnClick : false,
20099
20100     // private
20101     onRender : function(li){
20102         var s = document.createElement("span");
20103         s.className = this.itemCls;
20104         s.innerHTML = "&#160;";
20105         this.el = s;
20106         li.addClass("x-menu-sep-li");
20107         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20108     }
20109 });/*
20110  * Based on:
20111  * Ext JS Library 1.1.1
20112  * Copyright(c) 2006-2007, Ext JS, LLC.
20113  *
20114  * Originally Released Under LGPL - original licence link has changed is not relivant.
20115  *
20116  * Fork - LGPL
20117  * <script type="text/javascript">
20118  */
20119 /**
20120  * @class Roo.menu.Item
20121  * @extends Roo.menu.BaseItem
20122  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20123  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20124  * activation and click handling.
20125  * @constructor
20126  * Creates a new Item
20127  * @param {Object} config Configuration options
20128  */
20129 Roo.menu.Item = function(config){
20130     Roo.menu.Item.superclass.constructor.call(this, config);
20131     if(this.menu){
20132         this.menu = Roo.menu.MenuMgr.get(this.menu);
20133     }
20134 };
20135 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20136     
20137     /**
20138      * @cfg {String} text
20139      * The text to show on the menu item.
20140      */
20141     text: '',
20142      /**
20143      * @cfg {String} HTML to render in menu
20144      * The text to show on the menu item (HTML version).
20145      */
20146     html: '',
20147     /**
20148      * @cfg {String} icon
20149      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20150      */
20151     icon: undefined,
20152     /**
20153      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20154      */
20155     itemCls : "x-menu-item",
20156     /**
20157      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20158      */
20159     canActivate : true,
20160     /**
20161      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20162      */
20163     showDelay: 200,
20164     // doc'd in BaseItem
20165     hideDelay: 200,
20166
20167     // private
20168     ctype: "Roo.menu.Item",
20169     
20170     // private
20171     onRender : function(container, position){
20172         var el = document.createElement("a");
20173         el.hideFocus = true;
20174         el.unselectable = "on";
20175         el.href = this.href || "#";
20176         if(this.hrefTarget){
20177             el.target = this.hrefTarget;
20178         }
20179         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20180         
20181         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20182         
20183         el.innerHTML = String.format(
20184                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20185                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20186         this.el = el;
20187         Roo.menu.Item.superclass.onRender.call(this, container, position);
20188     },
20189
20190     /**
20191      * Sets the text to display in this menu item
20192      * @param {String} text The text to display
20193      * @param {Boolean} isHTML true to indicate text is pure html.
20194      */
20195     setText : function(text, isHTML){
20196         if (isHTML) {
20197             this.html = text;
20198         } else {
20199             this.text = text;
20200             this.html = '';
20201         }
20202         if(this.rendered){
20203             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20204      
20205             this.el.update(String.format(
20206                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20207                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20208             this.parentMenu.autoWidth();
20209         }
20210     },
20211
20212     // private
20213     handleClick : function(e){
20214         if(!this.href){ // if no link defined, stop the event automatically
20215             e.stopEvent();
20216         }
20217         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20218     },
20219
20220     // private
20221     activate : function(autoExpand){
20222         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20223             this.focus();
20224             if(autoExpand){
20225                 this.expandMenu();
20226             }
20227         }
20228         return true;
20229     },
20230
20231     // private
20232     shouldDeactivate : function(e){
20233         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20234             if(this.menu && this.menu.isVisible()){
20235                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20236             }
20237             return true;
20238         }
20239         return false;
20240     },
20241
20242     // private
20243     deactivate : function(){
20244         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20245         this.hideMenu();
20246     },
20247
20248     // private
20249     expandMenu : function(autoActivate){
20250         if(!this.disabled && this.menu){
20251             clearTimeout(this.hideTimer);
20252             delete this.hideTimer;
20253             if(!this.menu.isVisible() && !this.showTimer){
20254                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20255             }else if (this.menu.isVisible() && autoActivate){
20256                 this.menu.tryActivate(0, 1);
20257             }
20258         }
20259     },
20260
20261     // private
20262     deferExpand : function(autoActivate){
20263         delete this.showTimer;
20264         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20265         if(autoActivate){
20266             this.menu.tryActivate(0, 1);
20267         }
20268     },
20269
20270     // private
20271     hideMenu : function(){
20272         clearTimeout(this.showTimer);
20273         delete this.showTimer;
20274         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20275             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20276         }
20277     },
20278
20279     // private
20280     deferHide : function(){
20281         delete this.hideTimer;
20282         this.menu.hide();
20283     }
20284 });/*
20285  * Based on:
20286  * Ext JS Library 1.1.1
20287  * Copyright(c) 2006-2007, Ext JS, LLC.
20288  *
20289  * Originally Released Under LGPL - original licence link has changed is not relivant.
20290  *
20291  * Fork - LGPL
20292  * <script type="text/javascript">
20293  */
20294  
20295 /**
20296  * @class Roo.menu.CheckItem
20297  * @extends Roo.menu.Item
20298  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20299  * @constructor
20300  * Creates a new CheckItem
20301  * @param {Object} config Configuration options
20302  */
20303 Roo.menu.CheckItem = function(config){
20304     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20305     this.addEvents({
20306         /**
20307          * @event beforecheckchange
20308          * Fires before the checked value is set, providing an opportunity to cancel if needed
20309          * @param {Roo.menu.CheckItem} this
20310          * @param {Boolean} checked The new checked value that will be set
20311          */
20312         "beforecheckchange" : true,
20313         /**
20314          * @event checkchange
20315          * Fires after the checked value has been set
20316          * @param {Roo.menu.CheckItem} this
20317          * @param {Boolean} checked The checked value that was set
20318          */
20319         "checkchange" : true
20320     });
20321     if(this.checkHandler){
20322         this.on('checkchange', this.checkHandler, this.scope);
20323     }
20324 };
20325 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20326     /**
20327      * @cfg {String} group
20328      * All check items with the same group name will automatically be grouped into a single-select
20329      * radio button group (defaults to '')
20330      */
20331     /**
20332      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20333      */
20334     itemCls : "x-menu-item x-menu-check-item",
20335     /**
20336      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20337      */
20338     groupClass : "x-menu-group-item",
20339
20340     /**
20341      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20342      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20343      * initialized with checked = true will be rendered as checked.
20344      */
20345     checked: false,
20346
20347     // private
20348     ctype: "Roo.menu.CheckItem",
20349
20350     // private
20351     onRender : function(c){
20352         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20353         if(this.group){
20354             this.el.addClass(this.groupClass);
20355         }
20356         Roo.menu.MenuMgr.registerCheckable(this);
20357         if(this.checked){
20358             this.checked = false;
20359             this.setChecked(true, true);
20360         }
20361     },
20362
20363     // private
20364     destroy : function(){
20365         if(this.rendered){
20366             Roo.menu.MenuMgr.unregisterCheckable(this);
20367         }
20368         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20369     },
20370
20371     /**
20372      * Set the checked state of this item
20373      * @param {Boolean} checked The new checked value
20374      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20375      */
20376     setChecked : function(state, suppressEvent){
20377         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20378             if(this.container){
20379                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20380             }
20381             this.checked = state;
20382             if(suppressEvent !== true){
20383                 this.fireEvent("checkchange", this, state);
20384             }
20385         }
20386     },
20387
20388     // private
20389     handleClick : function(e){
20390        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20391            this.setChecked(!this.checked);
20392        }
20393        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20394     }
20395 });/*
20396  * Based on:
20397  * Ext JS Library 1.1.1
20398  * Copyright(c) 2006-2007, Ext JS, LLC.
20399  *
20400  * Originally Released Under LGPL - original licence link has changed is not relivant.
20401  *
20402  * Fork - LGPL
20403  * <script type="text/javascript">
20404  */
20405  
20406 /**
20407  * @class Roo.menu.DateItem
20408  * @extends Roo.menu.Adapter
20409  * A menu item that wraps the {@link Roo.DatPicker} component.
20410  * @constructor
20411  * Creates a new DateItem
20412  * @param {Object} config Configuration options
20413  */
20414 Roo.menu.DateItem = function(config){
20415     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20416     /** The Roo.DatePicker object @type Roo.DatePicker */
20417     this.picker = this.component;
20418     this.addEvents({select: true});
20419     
20420     this.picker.on("render", function(picker){
20421         picker.getEl().swallowEvent("click");
20422         picker.container.addClass("x-menu-date-item");
20423     });
20424
20425     this.picker.on("select", this.onSelect, this);
20426 };
20427
20428 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20429     // private
20430     onSelect : function(picker, date){
20431         this.fireEvent("select", this, date, picker);
20432         Roo.menu.DateItem.superclass.handleClick.call(this);
20433     }
20434 });/*
20435  * Based on:
20436  * Ext JS Library 1.1.1
20437  * Copyright(c) 2006-2007, Ext JS, LLC.
20438  *
20439  * Originally Released Under LGPL - original licence link has changed is not relivant.
20440  *
20441  * Fork - LGPL
20442  * <script type="text/javascript">
20443  */
20444  
20445 /**
20446  * @class Roo.menu.ColorItem
20447  * @extends Roo.menu.Adapter
20448  * A menu item that wraps the {@link Roo.ColorPalette} component.
20449  * @constructor
20450  * Creates a new ColorItem
20451  * @param {Object} config Configuration options
20452  */
20453 Roo.menu.ColorItem = function(config){
20454     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20455     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20456     this.palette = this.component;
20457     this.relayEvents(this.palette, ["select"]);
20458     if(this.selectHandler){
20459         this.on('select', this.selectHandler, this.scope);
20460     }
20461 };
20462 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20463  * Based on:
20464  * Ext JS Library 1.1.1
20465  * Copyright(c) 2006-2007, Ext JS, LLC.
20466  *
20467  * Originally Released Under LGPL - original licence link has changed is not relivant.
20468  *
20469  * Fork - LGPL
20470  * <script type="text/javascript">
20471  */
20472  
20473
20474 /**
20475  * @class Roo.menu.DateMenu
20476  * @extends Roo.menu.Menu
20477  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20478  * @constructor
20479  * Creates a new DateMenu
20480  * @param {Object} config Configuration options
20481  */
20482 Roo.menu.DateMenu = function(config){
20483     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20484     this.plain = true;
20485     var di = new Roo.menu.DateItem(config);
20486     this.add(di);
20487     /**
20488      * The {@link Roo.DatePicker} instance for this DateMenu
20489      * @type DatePicker
20490      */
20491     this.picker = di.picker;
20492     /**
20493      * @event select
20494      * @param {DatePicker} picker
20495      * @param {Date} date
20496      */
20497     this.relayEvents(di, ["select"]);
20498     this.on('beforeshow', function(){
20499         if(this.picker){
20500             this.picker.hideMonthPicker(false);
20501         }
20502     }, this);
20503 };
20504 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20505     cls:'x-date-menu'
20506 });/*
20507  * Based on:
20508  * Ext JS Library 1.1.1
20509  * Copyright(c) 2006-2007, Ext JS, LLC.
20510  *
20511  * Originally Released Under LGPL - original licence link has changed is not relivant.
20512  *
20513  * Fork - LGPL
20514  * <script type="text/javascript">
20515  */
20516  
20517
20518 /**
20519  * @class Roo.menu.ColorMenu
20520  * @extends Roo.menu.Menu
20521  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20522  * @constructor
20523  * Creates a new ColorMenu
20524  * @param {Object} config Configuration options
20525  */
20526 Roo.menu.ColorMenu = function(config){
20527     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20528     this.plain = true;
20529     var ci = new Roo.menu.ColorItem(config);
20530     this.add(ci);
20531     /**
20532      * The {@link Roo.ColorPalette} instance for this ColorMenu
20533      * @type ColorPalette
20534      */
20535     this.palette = ci.palette;
20536     /**
20537      * @event select
20538      * @param {ColorPalette} palette
20539      * @param {String} color
20540      */
20541     this.relayEvents(ci, ["select"]);
20542 };
20543 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20544  * Based on:
20545  * Ext JS Library 1.1.1
20546  * Copyright(c) 2006-2007, Ext JS, LLC.
20547  *
20548  * Originally Released Under LGPL - original licence link has changed is not relivant.
20549  *
20550  * Fork - LGPL
20551  * <script type="text/javascript">
20552  */
20553  
20554 /**
20555  * @class Roo.form.Field
20556  * @extends Roo.BoxComponent
20557  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20558  * @constructor
20559  * Creates a new Field
20560  * @param {Object} config Configuration options
20561  */
20562 Roo.form.Field = function(config){
20563     Roo.form.Field.superclass.constructor.call(this, config);
20564 };
20565
20566 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20567     /**
20568      * @cfg {String} fieldLabel Label to use when rendering a form.
20569      */
20570        /**
20571      * @cfg {String} qtip Mouse over tip
20572      */
20573      
20574     /**
20575      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20576      */
20577     invalidClass : "x-form-invalid",
20578     /**
20579      * @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")
20580      */
20581     invalidText : "The value in this field is invalid",
20582     /**
20583      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20584      */
20585     focusClass : "x-form-focus",
20586     /**
20587      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20588       automatic validation (defaults to "keyup").
20589      */
20590     validationEvent : "keyup",
20591     /**
20592      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20593      */
20594     validateOnBlur : true,
20595     /**
20596      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20597      */
20598     validationDelay : 250,
20599     /**
20600      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20601      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20602      */
20603     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
20604     /**
20605      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20606      */
20607     fieldClass : "x-form-field",
20608     /**
20609      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20610      *<pre>
20611 Value         Description
20612 -----------   ----------------------------------------------------------------------
20613 qtip          Display a quick tip when the user hovers over the field
20614 title         Display a default browser title attribute popup
20615 under         Add a block div beneath the field containing the error text
20616 side          Add an error icon to the right of the field with a popup on hover
20617 [element id]  Add the error text directly to the innerHTML of the specified element
20618 </pre>
20619      */
20620     msgTarget : 'qtip',
20621     /**
20622      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20623      */
20624     msgFx : 'normal',
20625
20626     /**
20627      * @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.
20628      */
20629     readOnly : false,
20630
20631     /**
20632      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20633      */
20634     disabled : false,
20635
20636     /**
20637      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20638      */
20639     inputType : undefined,
20640     
20641     /**
20642      * @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).
20643          */
20644         tabIndex : undefined,
20645         
20646     // private
20647     isFormField : true,
20648
20649     // private
20650     hasFocus : false,
20651     /**
20652      * @property {Roo.Element} fieldEl
20653      * Element Containing the rendered Field (with label etc.)
20654      */
20655     /**
20656      * @cfg {Mixed} value A value to initialize this field with.
20657      */
20658     value : undefined,
20659
20660     /**
20661      * @cfg {String} name The field's HTML name attribute.
20662      */
20663     /**
20664      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20665      */
20666
20667         // private ??
20668         initComponent : function(){
20669         Roo.form.Field.superclass.initComponent.call(this);
20670         this.addEvents({
20671             /**
20672              * @event focus
20673              * Fires when this field receives input focus.
20674              * @param {Roo.form.Field} this
20675              */
20676             focus : true,
20677             /**
20678              * @event blur
20679              * Fires when this field loses input focus.
20680              * @param {Roo.form.Field} this
20681              */
20682             blur : true,
20683             /**
20684              * @event specialkey
20685              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20686              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20687              * @param {Roo.form.Field} this
20688              * @param {Roo.EventObject} e The event object
20689              */
20690             specialkey : true,
20691             /**
20692              * @event change
20693              * Fires just before the field blurs if the field value has changed.
20694              * @param {Roo.form.Field} this
20695              * @param {Mixed} newValue The new value
20696              * @param {Mixed} oldValue The original value
20697              */
20698             change : true,
20699             /**
20700              * @event invalid
20701              * Fires after the field has been marked as invalid.
20702              * @param {Roo.form.Field} this
20703              * @param {String} msg The validation message
20704              */
20705             invalid : true,
20706             /**
20707              * @event valid
20708              * Fires after the field has been validated with no errors.
20709              * @param {Roo.form.Field} this
20710              */
20711             valid : true,
20712              /**
20713              * @event keyup
20714              * Fires after the key up
20715              * @param {Roo.form.Field} this
20716              * @param {Roo.EventObject}  e The event Object
20717              */
20718             keyup : true
20719         });
20720     },
20721
20722     /**
20723      * Returns the name attribute of the field if available
20724      * @return {String} name The field name
20725      */
20726     getName: function(){
20727          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
20728     },
20729
20730     // private
20731     onRender : function(ct, position){
20732         Roo.form.Field.superclass.onRender.call(this, ct, position);
20733         if(!this.el){
20734             var cfg = this.getAutoCreate();
20735             if(!cfg.name){
20736                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
20737             }
20738             if (!cfg.name.length) {
20739                 delete cfg.name;
20740             }
20741             if(this.inputType){
20742                 cfg.type = this.inputType;
20743             }
20744             this.el = ct.createChild(cfg, position);
20745         }
20746         var type = this.el.dom.type;
20747         if(type){
20748             if(type == 'password'){
20749                 type = 'text';
20750             }
20751             this.el.addClass('x-form-'+type);
20752         }
20753         if(this.readOnly){
20754             this.el.dom.readOnly = true;
20755         }
20756         if(this.tabIndex !== undefined){
20757             this.el.dom.setAttribute('tabIndex', this.tabIndex);
20758         }
20759
20760         this.el.addClass([this.fieldClass, this.cls]);
20761         this.initValue();
20762     },
20763
20764     /**
20765      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
20766      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
20767      * @return {Roo.form.Field} this
20768      */
20769     applyTo : function(target){
20770         this.allowDomMove = false;
20771         this.el = Roo.get(target);
20772         this.render(this.el.dom.parentNode);
20773         return this;
20774     },
20775
20776     // private
20777     initValue : function(){
20778         if(this.value !== undefined){
20779             this.setValue(this.value);
20780         }else if(this.el.dom.value.length > 0){
20781             this.setValue(this.el.dom.value);
20782         }
20783     },
20784
20785     /**
20786      * Returns true if this field has been changed since it was originally loaded and is not disabled.
20787      */
20788     isDirty : function() {
20789         if(this.disabled) {
20790             return false;
20791         }
20792         return String(this.getValue()) !== String(this.originalValue);
20793     },
20794
20795     // private
20796     afterRender : function(){
20797         Roo.form.Field.superclass.afterRender.call(this);
20798         this.initEvents();
20799     },
20800
20801     // private
20802     fireKey : function(e){
20803         //Roo.log('field ' + e.getKey());
20804         if(e.isNavKeyPress()){
20805             this.fireEvent("specialkey", this, e);
20806         }
20807     },
20808
20809     /**
20810      * Resets the current field value to the originally loaded value and clears any validation messages
20811      */
20812     reset : function(){
20813         this.setValue(this.resetValue);
20814         this.clearInvalid();
20815     },
20816
20817     // private
20818     initEvents : function(){
20819         // safari killled keypress - so keydown is now used..
20820         this.el.on("keydown" , this.fireKey,  this);
20821         this.el.on("focus", this.onFocus,  this);
20822         this.el.on("blur", this.onBlur,  this);
20823         this.el.relayEvent('keyup', this);
20824
20825         // reference to original value for reset
20826         this.originalValue = this.getValue();
20827         this.resetValue =  this.getValue();
20828     },
20829
20830     // private
20831     onFocus : function(){
20832         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20833             this.el.addClass(this.focusClass);
20834         }
20835         if(!this.hasFocus){
20836             this.hasFocus = true;
20837             this.startValue = this.getValue();
20838             this.fireEvent("focus", this);
20839         }
20840     },
20841
20842     beforeBlur : Roo.emptyFn,
20843
20844     // private
20845     onBlur : function(){
20846         this.beforeBlur();
20847         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20848             this.el.removeClass(this.focusClass);
20849         }
20850         this.hasFocus = false;
20851         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
20852             this.validate();
20853         }
20854         var v = this.getValue();
20855         if(String(v) !== String(this.startValue)){
20856             this.fireEvent('change', this, v, this.startValue);
20857         }
20858         this.fireEvent("blur", this);
20859     },
20860
20861     /**
20862      * Returns whether or not the field value is currently valid
20863      * @param {Boolean} preventMark True to disable marking the field invalid
20864      * @return {Boolean} True if the value is valid, else false
20865      */
20866     isValid : function(preventMark){
20867         if(this.disabled){
20868             return true;
20869         }
20870         var restore = this.preventMark;
20871         this.preventMark = preventMark === true;
20872         var v = this.validateValue(this.processValue(this.getRawValue()));
20873         this.preventMark = restore;
20874         return v;
20875     },
20876
20877     /**
20878      * Validates the field value
20879      * @return {Boolean} True if the value is valid, else false
20880      */
20881     validate : function(){
20882         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
20883             this.clearInvalid();
20884             return true;
20885         }
20886         return false;
20887     },
20888
20889     processValue : function(value){
20890         return value;
20891     },
20892
20893     // private
20894     // Subclasses should provide the validation implementation by overriding this
20895     validateValue : function(value){
20896         return true;
20897     },
20898
20899     /**
20900      * Mark this field as invalid
20901      * @param {String} msg The validation message
20902      */
20903     markInvalid : function(msg){
20904         if(!this.rendered || this.preventMark){ // not rendered
20905             return;
20906         }
20907         
20908         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
20909         
20910         obj.el.addClass(this.invalidClass);
20911         msg = msg || this.invalidText;
20912         switch(this.msgTarget){
20913             case 'qtip':
20914                 obj.el.dom.qtip = msg;
20915                 obj.el.dom.qclass = 'x-form-invalid-tip';
20916                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
20917                     Roo.QuickTips.enable();
20918                 }
20919                 break;
20920             case 'title':
20921                 this.el.dom.title = msg;
20922                 break;
20923             case 'under':
20924                 if(!this.errorEl){
20925                     var elp = this.el.findParent('.x-form-element', 5, true);
20926                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
20927                     this.errorEl.setWidth(elp.getWidth(true)-20);
20928                 }
20929                 this.errorEl.update(msg);
20930                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
20931                 break;
20932             case 'side':
20933                 if(!this.errorIcon){
20934                     var elp = this.el.findParent('.x-form-element', 5, true);
20935                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
20936                 }
20937                 this.alignErrorIcon();
20938                 this.errorIcon.dom.qtip = msg;
20939                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
20940                 this.errorIcon.show();
20941                 this.on('resize', this.alignErrorIcon, this);
20942                 break;
20943             default:
20944                 var t = Roo.getDom(this.msgTarget);
20945                 t.innerHTML = msg;
20946                 t.style.display = this.msgDisplay;
20947                 break;
20948         }
20949         this.fireEvent('invalid', this, msg);
20950     },
20951
20952     // private
20953     alignErrorIcon : function(){
20954         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
20955     },
20956
20957     /**
20958      * Clear any invalid styles/messages for this field
20959      */
20960     clearInvalid : function(){
20961         if(!this.rendered || this.preventMark){ // not rendered
20962             return;
20963         }
20964         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
20965         
20966         obj.el.removeClass(this.invalidClass);
20967         switch(this.msgTarget){
20968             case 'qtip':
20969                 obj.el.dom.qtip = '';
20970                 break;
20971             case 'title':
20972                 this.el.dom.title = '';
20973                 break;
20974             case 'under':
20975                 if(this.errorEl){
20976                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
20977                 }
20978                 break;
20979             case 'side':
20980                 if(this.errorIcon){
20981                     this.errorIcon.dom.qtip = '';
20982                     this.errorIcon.hide();
20983                     this.un('resize', this.alignErrorIcon, this);
20984                 }
20985                 break;
20986             default:
20987                 var t = Roo.getDom(this.msgTarget);
20988                 t.innerHTML = '';
20989                 t.style.display = 'none';
20990                 break;
20991         }
20992         this.fireEvent('valid', this);
20993     },
20994
20995     /**
20996      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
20997      * @return {Mixed} value The field value
20998      */
20999     getRawValue : function(){
21000         var v = this.el.getValue();
21001         
21002         return v;
21003     },
21004
21005     /**
21006      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21007      * @return {Mixed} value The field value
21008      */
21009     getValue : function(){
21010         var v = this.el.getValue();
21011          
21012         return v;
21013     },
21014
21015     /**
21016      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21017      * @param {Mixed} value The value to set
21018      */
21019     setRawValue : function(v){
21020         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21021     },
21022
21023     /**
21024      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21025      * @param {Mixed} value The value to set
21026      */
21027     setValue : function(v){
21028         this.value = v;
21029         if(this.rendered){
21030             this.el.dom.value = (v === null || v === undefined ? '' : v);
21031              this.validate();
21032         }
21033     },
21034
21035     adjustSize : function(w, h){
21036         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21037         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21038         return s;
21039     },
21040
21041     adjustWidth : function(tag, w){
21042         tag = tag.toLowerCase();
21043         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21044             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21045                 if(tag == 'input'){
21046                     return w + 2;
21047                 }
21048                 if(tag == 'textarea'){
21049                     return w-2;
21050                 }
21051             }else if(Roo.isOpera){
21052                 if(tag == 'input'){
21053                     return w + 2;
21054                 }
21055                 if(tag == 'textarea'){
21056                     return w-2;
21057                 }
21058             }
21059         }
21060         return w;
21061     }
21062 });
21063
21064
21065 // anything other than normal should be considered experimental
21066 Roo.form.Field.msgFx = {
21067     normal : {
21068         show: function(msgEl, f){
21069             msgEl.setDisplayed('block');
21070         },
21071
21072         hide : function(msgEl, f){
21073             msgEl.setDisplayed(false).update('');
21074         }
21075     },
21076
21077     slide : {
21078         show: function(msgEl, f){
21079             msgEl.slideIn('t', {stopFx:true});
21080         },
21081
21082         hide : function(msgEl, f){
21083             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21084         }
21085     },
21086
21087     slideRight : {
21088         show: function(msgEl, f){
21089             msgEl.fixDisplay();
21090             msgEl.alignTo(f.el, 'tl-tr');
21091             msgEl.slideIn('l', {stopFx:true});
21092         },
21093
21094         hide : function(msgEl, f){
21095             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21096         }
21097     }
21098 };/*
21099  * Based on:
21100  * Ext JS Library 1.1.1
21101  * Copyright(c) 2006-2007, Ext JS, LLC.
21102  *
21103  * Originally Released Under LGPL - original licence link has changed is not relivant.
21104  *
21105  * Fork - LGPL
21106  * <script type="text/javascript">
21107  */
21108  
21109
21110 /**
21111  * @class Roo.form.TextField
21112  * @extends Roo.form.Field
21113  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21114  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21115  * @constructor
21116  * Creates a new TextField
21117  * @param {Object} config Configuration options
21118  */
21119 Roo.form.TextField = function(config){
21120     Roo.form.TextField.superclass.constructor.call(this, config);
21121     this.addEvents({
21122         /**
21123          * @event autosize
21124          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21125          * according to the default logic, but this event provides a hook for the developer to apply additional
21126          * logic at runtime to resize the field if needed.
21127              * @param {Roo.form.Field} this This text field
21128              * @param {Number} width The new field width
21129              */
21130         autosize : true
21131     });
21132 };
21133
21134 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21135     /**
21136      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21137      */
21138     grow : false,
21139     /**
21140      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21141      */
21142     growMin : 30,
21143     /**
21144      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21145      */
21146     growMax : 800,
21147     /**
21148      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21149      */
21150     vtype : null,
21151     /**
21152      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21153      */
21154     maskRe : null,
21155     /**
21156      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21157      */
21158     disableKeyFilter : false,
21159     /**
21160      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21161      */
21162     allowBlank : true,
21163     /**
21164      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21165      */
21166     minLength : 0,
21167     /**
21168      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21169      */
21170     maxLength : Number.MAX_VALUE,
21171     /**
21172      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21173      */
21174     minLengthText : "The minimum length for this field is {0}",
21175     /**
21176      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21177      */
21178     maxLengthText : "The maximum length for this field is {0}",
21179     /**
21180      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21181      */
21182     selectOnFocus : false,
21183     /**
21184      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21185      */
21186     blankText : "This field is required",
21187     /**
21188      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21189      * If available, this function will be called only after the basic validators all return true, and will be passed the
21190      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21191      */
21192     validator : null,
21193     /**
21194      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21195      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21196      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21197      */
21198     regex : null,
21199     /**
21200      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21201      */
21202     regexText : "",
21203     /**
21204      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
21205      */
21206     emptyText : null,
21207    
21208
21209     // private
21210     initEvents : function()
21211     {
21212         if (this.emptyText) {
21213             this.el.attr('placeholder', this.emptyText);
21214         }
21215         
21216         Roo.form.TextField.superclass.initEvents.call(this);
21217         if(this.validationEvent == 'keyup'){
21218             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21219             this.el.on('keyup', this.filterValidation, this);
21220         }
21221         else if(this.validationEvent !== false){
21222             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21223         }
21224         
21225         if(this.selectOnFocus){
21226             this.on("focus", this.preFocus, this);
21227             
21228         }
21229         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21230             this.el.on("keypress", this.filterKeys, this);
21231         }
21232         if(this.grow){
21233             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21234             this.el.on("click", this.autoSize,  this);
21235         }
21236         if(this.el.is('input[type=password]') && Roo.isSafari){
21237             this.el.on('keydown', this.SafariOnKeyDown, this);
21238         }
21239     },
21240
21241     processValue : function(value){
21242         if(this.stripCharsRe){
21243             var newValue = value.replace(this.stripCharsRe, '');
21244             if(newValue !== value){
21245                 this.setRawValue(newValue);
21246                 return newValue;
21247             }
21248         }
21249         return value;
21250     },
21251
21252     filterValidation : function(e){
21253         if(!e.isNavKeyPress()){
21254             this.validationTask.delay(this.validationDelay);
21255         }
21256     },
21257
21258     // private
21259     onKeyUp : function(e){
21260         if(!e.isNavKeyPress()){
21261             this.autoSize();
21262         }
21263     },
21264
21265     /**
21266      * Resets the current field value to the originally-loaded value and clears any validation messages.
21267      *  
21268      */
21269     reset : function(){
21270         Roo.form.TextField.superclass.reset.call(this);
21271        
21272     },
21273
21274     
21275     // private
21276     preFocus : function(){
21277         
21278         if(this.selectOnFocus){
21279             this.el.dom.select();
21280         }
21281     },
21282
21283     
21284     // private
21285     filterKeys : function(e){
21286         var k = e.getKey();
21287         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21288             return;
21289         }
21290         var c = e.getCharCode(), cc = String.fromCharCode(c);
21291         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21292             return;
21293         }
21294         if(!this.maskRe.test(cc)){
21295             e.stopEvent();
21296         }
21297     },
21298
21299     setValue : function(v){
21300         
21301         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21302         
21303         this.autoSize();
21304     },
21305
21306     /**
21307      * Validates a value according to the field's validation rules and marks the field as invalid
21308      * if the validation fails
21309      * @param {Mixed} value The value to validate
21310      * @return {Boolean} True if the value is valid, else false
21311      */
21312     validateValue : function(value){
21313         if(value.length < 1)  { // if it's blank
21314              if(this.allowBlank){
21315                 this.clearInvalid();
21316                 return true;
21317              }else{
21318                 this.markInvalid(this.blankText);
21319                 return false;
21320              }
21321         }
21322         if(value.length < this.minLength){
21323             this.markInvalid(String.format(this.minLengthText, this.minLength));
21324             return false;
21325         }
21326         if(value.length > this.maxLength){
21327             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21328             return false;
21329         }
21330         if(this.vtype){
21331             var vt = Roo.form.VTypes;
21332             if(!vt[this.vtype](value, this)){
21333                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21334                 return false;
21335             }
21336         }
21337         if(typeof this.validator == "function"){
21338             var msg = this.validator(value);
21339             if(msg !== true){
21340                 this.markInvalid(msg);
21341                 return false;
21342             }
21343         }
21344         if(this.regex && !this.regex.test(value)){
21345             this.markInvalid(this.regexText);
21346             return false;
21347         }
21348         return true;
21349     },
21350
21351     /**
21352      * Selects text in this field
21353      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21354      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21355      */
21356     selectText : function(start, end){
21357         var v = this.getRawValue();
21358         if(v.length > 0){
21359             start = start === undefined ? 0 : start;
21360             end = end === undefined ? v.length : end;
21361             var d = this.el.dom;
21362             if(d.setSelectionRange){
21363                 d.setSelectionRange(start, end);
21364             }else if(d.createTextRange){
21365                 var range = d.createTextRange();
21366                 range.moveStart("character", start);
21367                 range.moveEnd("character", v.length-end);
21368                 range.select();
21369             }
21370         }
21371     },
21372
21373     /**
21374      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21375      * This only takes effect if grow = true, and fires the autosize event.
21376      */
21377     autoSize : function(){
21378         if(!this.grow || !this.rendered){
21379             return;
21380         }
21381         if(!this.metrics){
21382             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21383         }
21384         var el = this.el;
21385         var v = el.dom.value;
21386         var d = document.createElement('div');
21387         d.appendChild(document.createTextNode(v));
21388         v = d.innerHTML;
21389         d = null;
21390         v += "&#160;";
21391         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21392         this.el.setWidth(w);
21393         this.fireEvent("autosize", this, w);
21394     },
21395     
21396     // private
21397     SafariOnKeyDown : function(event)
21398     {
21399         // this is a workaround for a password hang bug on chrome/ webkit.
21400         
21401         var isSelectAll = false;
21402         
21403         if(this.el.dom.selectionEnd > 0){
21404             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
21405         }
21406         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
21407             event.preventDefault();
21408             this.setValue('');
21409             return;
21410         }
21411         
21412         if(isSelectAll){ // backspace and delete key
21413             
21414             event.preventDefault();
21415             // this is very hacky as keydown always get's upper case.
21416             //
21417             var cc = String.fromCharCode(event.getCharCode());
21418             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
21419             
21420         }
21421         
21422         
21423     }
21424 });/*
21425  * Based on:
21426  * Ext JS Library 1.1.1
21427  * Copyright(c) 2006-2007, Ext JS, LLC.
21428  *
21429  * Originally Released Under LGPL - original licence link has changed is not relivant.
21430  *
21431  * Fork - LGPL
21432  * <script type="text/javascript">
21433  */
21434  
21435 /**
21436  * @class Roo.form.Hidden
21437  * @extends Roo.form.TextField
21438  * Simple Hidden element used on forms 
21439  * 
21440  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21441  * 
21442  * @constructor
21443  * Creates a new Hidden form element.
21444  * @param {Object} config Configuration options
21445  */
21446
21447
21448
21449 // easy hidden field...
21450 Roo.form.Hidden = function(config){
21451     Roo.form.Hidden.superclass.constructor.call(this, config);
21452 };
21453   
21454 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21455     fieldLabel:      '',
21456     inputType:      'hidden',
21457     width:          50,
21458     allowBlank:     true,
21459     labelSeparator: '',
21460     hidden:         true,
21461     itemCls :       'x-form-item-display-none'
21462
21463
21464 });
21465
21466
21467 /*
21468  * Based on:
21469  * Ext JS Library 1.1.1
21470  * Copyright(c) 2006-2007, Ext JS, LLC.
21471  *
21472  * Originally Released Under LGPL - original licence link has changed is not relivant.
21473  *
21474  * Fork - LGPL
21475  * <script type="text/javascript">
21476  */
21477  
21478 /**
21479  * @class Roo.form.TriggerField
21480  * @extends Roo.form.TextField
21481  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21482  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21483  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21484  * for which you can provide a custom implementation.  For example:
21485  * <pre><code>
21486 var trigger = new Roo.form.TriggerField();
21487 trigger.onTriggerClick = myTriggerFn;
21488 trigger.applyTo('my-field');
21489 </code></pre>
21490  *
21491  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21492  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21493  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21494  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21495  * @constructor
21496  * Create a new TriggerField.
21497  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21498  * to the base TextField)
21499  */
21500 Roo.form.TriggerField = function(config){
21501     this.mimicing = false;
21502     Roo.form.TriggerField.superclass.constructor.call(this, config);
21503 };
21504
21505 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21506     /**
21507      * @cfg {String} triggerClass A CSS class to apply to the trigger
21508      */
21509     /**
21510      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21511      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21512      */
21513     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
21514     /**
21515      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21516      */
21517     hideTrigger:false,
21518
21519     /** @cfg {Boolean} grow @hide */
21520     /** @cfg {Number} growMin @hide */
21521     /** @cfg {Number} growMax @hide */
21522
21523     /**
21524      * @hide 
21525      * @method
21526      */
21527     autoSize: Roo.emptyFn,
21528     // private
21529     monitorTab : true,
21530     // private
21531     deferHeight : true,
21532
21533     
21534     actionMode : 'wrap',
21535     // private
21536     onResize : function(w, h){
21537         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21538         if(typeof w == 'number'){
21539             var x = w - this.trigger.getWidth();
21540             this.el.setWidth(this.adjustWidth('input', x));
21541             this.trigger.setStyle('left', x+'px');
21542         }
21543     },
21544
21545     // private
21546     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21547
21548     // private
21549     getResizeEl : function(){
21550         return this.wrap;
21551     },
21552
21553     // private
21554     getPositionEl : function(){
21555         return this.wrap;
21556     },
21557
21558     // private
21559     alignErrorIcon : function(){
21560         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21561     },
21562
21563     // private
21564     onRender : function(ct, position){
21565         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21566         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21567         this.trigger = this.wrap.createChild(this.triggerConfig ||
21568                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21569         if(this.hideTrigger){
21570             this.trigger.setDisplayed(false);
21571         }
21572         this.initTrigger();
21573         if(!this.width){
21574             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21575         }
21576     },
21577
21578     // private
21579     initTrigger : function(){
21580         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21581         this.trigger.addClassOnOver('x-form-trigger-over');
21582         this.trigger.addClassOnClick('x-form-trigger-click');
21583     },
21584
21585     // private
21586     onDestroy : function(){
21587         if(this.trigger){
21588             this.trigger.removeAllListeners();
21589             this.trigger.remove();
21590         }
21591         if(this.wrap){
21592             this.wrap.remove();
21593         }
21594         Roo.form.TriggerField.superclass.onDestroy.call(this);
21595     },
21596
21597     // private
21598     onFocus : function(){
21599         Roo.form.TriggerField.superclass.onFocus.call(this);
21600         if(!this.mimicing){
21601             this.wrap.addClass('x-trigger-wrap-focus');
21602             this.mimicing = true;
21603             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21604             if(this.monitorTab){
21605                 this.el.on("keydown", this.checkTab, this);
21606             }
21607         }
21608     },
21609
21610     // private
21611     checkTab : function(e){
21612         if(e.getKey() == e.TAB){
21613             this.triggerBlur();
21614         }
21615     },
21616
21617     // private
21618     onBlur : function(){
21619         // do nothing
21620     },
21621
21622     // private
21623     mimicBlur : function(e, t){
21624         if(!this.wrap.contains(t) && this.validateBlur()){
21625             this.triggerBlur();
21626         }
21627     },
21628
21629     // private
21630     triggerBlur : function(){
21631         this.mimicing = false;
21632         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21633         if(this.monitorTab){
21634             this.el.un("keydown", this.checkTab, this);
21635         }
21636         this.wrap.removeClass('x-trigger-wrap-focus');
21637         Roo.form.TriggerField.superclass.onBlur.call(this);
21638     },
21639
21640     // private
21641     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21642     validateBlur : function(e, t){
21643         return true;
21644     },
21645
21646     // private
21647     onDisable : function(){
21648         Roo.form.TriggerField.superclass.onDisable.call(this);
21649         if(this.wrap){
21650             this.wrap.addClass('x-item-disabled');
21651         }
21652     },
21653
21654     // private
21655     onEnable : function(){
21656         Roo.form.TriggerField.superclass.onEnable.call(this);
21657         if(this.wrap){
21658             this.wrap.removeClass('x-item-disabled');
21659         }
21660     },
21661
21662     // private
21663     onShow : function(){
21664         var ae = this.getActionEl();
21665         
21666         if(ae){
21667             ae.dom.style.display = '';
21668             ae.dom.style.visibility = 'visible';
21669         }
21670     },
21671
21672     // private
21673     
21674     onHide : function(){
21675         var ae = this.getActionEl();
21676         ae.dom.style.display = 'none';
21677     },
21678
21679     /**
21680      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
21681      * by an implementing function.
21682      * @method
21683      * @param {EventObject} e
21684      */
21685     onTriggerClick : Roo.emptyFn
21686 });
21687
21688 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
21689 // to be extended by an implementing class.  For an example of implementing this class, see the custom
21690 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21691 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21692     initComponent : function(){
21693         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21694
21695         this.triggerConfig = {
21696             tag:'span', cls:'x-form-twin-triggers', cn:[
21697             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21698             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21699         ]};
21700     },
21701
21702     getTrigger : function(index){
21703         return this.triggers[index];
21704     },
21705
21706     initTrigger : function(){
21707         var ts = this.trigger.select('.x-form-trigger', true);
21708         this.wrap.setStyle('overflow', 'hidden');
21709         var triggerField = this;
21710         ts.each(function(t, all, index){
21711             t.hide = function(){
21712                 var w = triggerField.wrap.getWidth();
21713                 this.dom.style.display = 'none';
21714                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21715             };
21716             t.show = function(){
21717                 var w = triggerField.wrap.getWidth();
21718                 this.dom.style.display = '';
21719                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21720             };
21721             var triggerIndex = 'Trigger'+(index+1);
21722
21723             if(this['hide'+triggerIndex]){
21724                 t.dom.style.display = 'none';
21725             }
21726             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
21727             t.addClassOnOver('x-form-trigger-over');
21728             t.addClassOnClick('x-form-trigger-click');
21729         }, this);
21730         this.triggers = ts.elements;
21731     },
21732
21733     onTrigger1Click : Roo.emptyFn,
21734     onTrigger2Click : Roo.emptyFn
21735 });/*
21736  * Based on:
21737  * Ext JS Library 1.1.1
21738  * Copyright(c) 2006-2007, Ext JS, LLC.
21739  *
21740  * Originally Released Under LGPL - original licence link has changed is not relivant.
21741  *
21742  * Fork - LGPL
21743  * <script type="text/javascript">
21744  */
21745  
21746 /**
21747  * @class Roo.form.TextArea
21748  * @extends Roo.form.TextField
21749  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
21750  * support for auto-sizing.
21751  * @constructor
21752  * Creates a new TextArea
21753  * @param {Object} config Configuration options
21754  */
21755 Roo.form.TextArea = function(config){
21756     Roo.form.TextArea.superclass.constructor.call(this, config);
21757     // these are provided exchanges for backwards compat
21758     // minHeight/maxHeight were replaced by growMin/growMax to be
21759     // compatible with TextField growing config values
21760     if(this.minHeight !== undefined){
21761         this.growMin = this.minHeight;
21762     }
21763     if(this.maxHeight !== undefined){
21764         this.growMax = this.maxHeight;
21765     }
21766 };
21767
21768 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
21769     /**
21770      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
21771      */
21772     growMin : 60,
21773     /**
21774      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
21775      */
21776     growMax: 1000,
21777     /**
21778      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
21779      * in the field (equivalent to setting overflow: hidden, defaults to false)
21780      */
21781     preventScrollbars: false,
21782     /**
21783      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21784      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
21785      */
21786
21787     // private
21788     onRender : function(ct, position){
21789         if(!this.el){
21790             this.defaultAutoCreate = {
21791                 tag: "textarea",
21792                 style:"width:300px;height:60px;",
21793                 autocomplete: "off"
21794             };
21795         }
21796         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
21797         if(this.grow){
21798             this.textSizeEl = Roo.DomHelper.append(document.body, {
21799                 tag: "pre", cls: "x-form-grow-sizer"
21800             });
21801             if(this.preventScrollbars){
21802                 this.el.setStyle("overflow", "hidden");
21803             }
21804             this.el.setHeight(this.growMin);
21805         }
21806     },
21807
21808     onDestroy : function(){
21809         if(this.textSizeEl){
21810             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
21811         }
21812         Roo.form.TextArea.superclass.onDestroy.call(this);
21813     },
21814
21815     // private
21816     onKeyUp : function(e){
21817         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
21818             this.autoSize();
21819         }
21820     },
21821
21822     /**
21823      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
21824      * This only takes effect if grow = true, and fires the autosize event if the height changes.
21825      */
21826     autoSize : function(){
21827         if(!this.grow || !this.textSizeEl){
21828             return;
21829         }
21830         var el = this.el;
21831         var v = el.dom.value;
21832         var ts = this.textSizeEl;
21833
21834         ts.innerHTML = '';
21835         ts.appendChild(document.createTextNode(v));
21836         v = ts.innerHTML;
21837
21838         Roo.fly(ts).setWidth(this.el.getWidth());
21839         if(v.length < 1){
21840             v = "&#160;&#160;";
21841         }else{
21842             if(Roo.isIE){
21843                 v = v.replace(/\n/g, '<p>&#160;</p>');
21844             }
21845             v += "&#160;\n&#160;";
21846         }
21847         ts.innerHTML = v;
21848         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
21849         if(h != this.lastHeight){
21850             this.lastHeight = h;
21851             this.el.setHeight(h);
21852             this.fireEvent("autosize", this, h);
21853         }
21854     }
21855 });/*
21856  * Based on:
21857  * Ext JS Library 1.1.1
21858  * Copyright(c) 2006-2007, Ext JS, LLC.
21859  *
21860  * Originally Released Under LGPL - original licence link has changed is not relivant.
21861  *
21862  * Fork - LGPL
21863  * <script type="text/javascript">
21864  */
21865  
21866
21867 /**
21868  * @class Roo.form.NumberField
21869  * @extends Roo.form.TextField
21870  * Numeric text field that provides automatic keystroke filtering and numeric validation.
21871  * @constructor
21872  * Creates a new NumberField
21873  * @param {Object} config Configuration options
21874  */
21875 Roo.form.NumberField = function(config){
21876     Roo.form.NumberField.superclass.constructor.call(this, config);
21877 };
21878
21879 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
21880     /**
21881      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
21882      */
21883     fieldClass: "x-form-field x-form-num-field",
21884     /**
21885      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
21886      */
21887     allowDecimals : true,
21888     /**
21889      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
21890      */
21891     decimalSeparator : ".",
21892     /**
21893      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
21894      */
21895     decimalPrecision : 2,
21896     /**
21897      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
21898      */
21899     allowNegative : true,
21900     /**
21901      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
21902      */
21903     minValue : Number.NEGATIVE_INFINITY,
21904     /**
21905      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
21906      */
21907     maxValue : Number.MAX_VALUE,
21908     /**
21909      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
21910      */
21911     minText : "The minimum value for this field is {0}",
21912     /**
21913      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
21914      */
21915     maxText : "The maximum value for this field is {0}",
21916     /**
21917      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
21918      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
21919      */
21920     nanText : "{0} is not a valid number",
21921
21922     // private
21923     initEvents : function(){
21924         Roo.form.NumberField.superclass.initEvents.call(this);
21925         var allowed = "0123456789";
21926         if(this.allowDecimals){
21927             allowed += this.decimalSeparator;
21928         }
21929         if(this.allowNegative){
21930             allowed += "-";
21931         }
21932         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
21933         var keyPress = function(e){
21934             var k = e.getKey();
21935             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
21936                 return;
21937             }
21938             var c = e.getCharCode();
21939             if(allowed.indexOf(String.fromCharCode(c)) === -1){
21940                 e.stopEvent();
21941             }
21942         };
21943         this.el.on("keypress", keyPress, this);
21944     },
21945
21946     // private
21947     validateValue : function(value){
21948         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
21949             return false;
21950         }
21951         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
21952              return true;
21953         }
21954         var num = this.parseValue(value);
21955         if(isNaN(num)){
21956             this.markInvalid(String.format(this.nanText, value));
21957             return false;
21958         }
21959         if(num < this.minValue){
21960             this.markInvalid(String.format(this.minText, this.minValue));
21961             return false;
21962         }
21963         if(num > this.maxValue){
21964             this.markInvalid(String.format(this.maxText, this.maxValue));
21965             return false;
21966         }
21967         return true;
21968     },
21969
21970     getValue : function(){
21971         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
21972     },
21973
21974     // private
21975     parseValue : function(value){
21976         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
21977         return isNaN(value) ? '' : value;
21978     },
21979
21980     // private
21981     fixPrecision : function(value){
21982         var nan = isNaN(value);
21983         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
21984             return nan ? '' : value;
21985         }
21986         return parseFloat(value).toFixed(this.decimalPrecision);
21987     },
21988
21989     setValue : function(v){
21990         v = this.fixPrecision(v);
21991         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
21992     },
21993
21994     // private
21995     decimalPrecisionFcn : function(v){
21996         return Math.floor(v);
21997     },
21998
21999     beforeBlur : function(){
22000         var v = this.parseValue(this.getRawValue());
22001         if(v){
22002             this.setValue(v);
22003         }
22004     }
22005 });/*
22006  * Based on:
22007  * Ext JS Library 1.1.1
22008  * Copyright(c) 2006-2007, Ext JS, LLC.
22009  *
22010  * Originally Released Under LGPL - original licence link has changed is not relivant.
22011  *
22012  * Fork - LGPL
22013  * <script type="text/javascript">
22014  */
22015  
22016 /**
22017  * @class Roo.form.DateField
22018  * @extends Roo.form.TriggerField
22019  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22020 * @constructor
22021 * Create a new DateField
22022 * @param {Object} config
22023  */
22024 Roo.form.DateField = function(config){
22025     Roo.form.DateField.superclass.constructor.call(this, config);
22026     
22027       this.addEvents({
22028          
22029         /**
22030          * @event select
22031          * Fires when a date is selected
22032              * @param {Roo.form.DateField} combo This combo box
22033              * @param {Date} date The date selected
22034              */
22035         'select' : true
22036          
22037     });
22038     
22039     
22040     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22041     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22042     this.ddMatch = null;
22043     if(this.disabledDates){
22044         var dd = this.disabledDates;
22045         var re = "(?:";
22046         for(var i = 0; i < dd.length; i++){
22047             re += dd[i];
22048             if(i != dd.length-1) re += "|";
22049         }
22050         this.ddMatch = new RegExp(re + ")");
22051     }
22052 };
22053
22054 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22055     /**
22056      * @cfg {String} format
22057      * The default date format string which can be overriden for localization support.  The format must be
22058      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22059      */
22060     format : "m/d/y",
22061     /**
22062      * @cfg {String} altFormats
22063      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22064      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22065      */
22066     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22067     /**
22068      * @cfg {Array} disabledDays
22069      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22070      */
22071     disabledDays : null,
22072     /**
22073      * @cfg {String} disabledDaysText
22074      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22075      */
22076     disabledDaysText : "Disabled",
22077     /**
22078      * @cfg {Array} disabledDates
22079      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22080      * expression so they are very powerful. Some examples:
22081      * <ul>
22082      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22083      * <li>["03/08", "09/16"] would disable those days for every year</li>
22084      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22085      * <li>["03/../2006"] would disable every day in March 2006</li>
22086      * <li>["^03"] would disable every day in every March</li>
22087      * </ul>
22088      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22089      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22090      */
22091     disabledDates : null,
22092     /**
22093      * @cfg {String} disabledDatesText
22094      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22095      */
22096     disabledDatesText : "Disabled",
22097     /**
22098      * @cfg {Date/String} minValue
22099      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22100      * valid format (defaults to null).
22101      */
22102     minValue : null,
22103     /**
22104      * @cfg {Date/String} maxValue
22105      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22106      * valid format (defaults to null).
22107      */
22108     maxValue : null,
22109     /**
22110      * @cfg {String} minText
22111      * The error text to display when the date in the cell is before minValue (defaults to
22112      * 'The date in this field must be after {minValue}').
22113      */
22114     minText : "The date in this field must be equal to or after {0}",
22115     /**
22116      * @cfg {String} maxText
22117      * The error text to display when the date in the cell is after maxValue (defaults to
22118      * 'The date in this field must be before {maxValue}').
22119      */
22120     maxText : "The date in this field must be equal to or before {0}",
22121     /**
22122      * @cfg {String} invalidText
22123      * The error text to display when the date in the field is invalid (defaults to
22124      * '{value} is not a valid date - it must be in the format {format}').
22125      */
22126     invalidText : "{0} is not a valid date - it must be in the format {1}",
22127     /**
22128      * @cfg {String} triggerClass
22129      * An additional CSS class used to style the trigger button.  The trigger will always get the
22130      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22131      * which displays a calendar icon).
22132      */
22133     triggerClass : 'x-form-date-trigger',
22134     
22135
22136     /**
22137      * @cfg {Boolean} useIso
22138      * if enabled, then the date field will use a hidden field to store the 
22139      * real value as iso formated date. default (false)
22140      */ 
22141     useIso : false,
22142     /**
22143      * @cfg {String/Object} autoCreate
22144      * A DomHelper element spec, or true for a default element spec (defaults to
22145      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22146      */ 
22147     // private
22148     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22149     
22150     // private
22151     hiddenField: false,
22152     
22153     onRender : function(ct, position)
22154     {
22155         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22156         if (this.useIso) {
22157             //this.el.dom.removeAttribute('name'); 
22158             Roo.log("Changing name?");
22159             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
22160             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22161                     'before', true);
22162             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22163             // prevent input submission
22164             this.hiddenName = this.name;
22165         }
22166             
22167             
22168     },
22169     
22170     // private
22171     validateValue : function(value)
22172     {
22173         value = this.formatDate(value);
22174         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22175             Roo.log('super failed');
22176             return false;
22177         }
22178         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22179              return true;
22180         }
22181         var svalue = value;
22182         value = this.parseDate(value);
22183         if(!value){
22184             Roo.log('parse date failed' + svalue);
22185             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22186             return false;
22187         }
22188         var time = value.getTime();
22189         if(this.minValue && time < this.minValue.getTime()){
22190             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22191             return false;
22192         }
22193         if(this.maxValue && time > this.maxValue.getTime()){
22194             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22195             return false;
22196         }
22197         if(this.disabledDays){
22198             var day = value.getDay();
22199             for(var i = 0; i < this.disabledDays.length; i++) {
22200                 if(day === this.disabledDays[i]){
22201                     this.markInvalid(this.disabledDaysText);
22202                     return false;
22203                 }
22204             }
22205         }
22206         var fvalue = this.formatDate(value);
22207         if(this.ddMatch && this.ddMatch.test(fvalue)){
22208             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22209             return false;
22210         }
22211         return true;
22212     },
22213
22214     // private
22215     // Provides logic to override the default TriggerField.validateBlur which just returns true
22216     validateBlur : function(){
22217         return !this.menu || !this.menu.isVisible();
22218     },
22219     
22220     getName: function()
22221     {
22222         // returns hidden if it's set..
22223         if (!this.rendered) {return ''};
22224         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
22225         
22226     },
22227
22228     /**
22229      * Returns the current date value of the date field.
22230      * @return {Date} The date value
22231      */
22232     getValue : function(){
22233         
22234         return  this.hiddenField ?
22235                 this.hiddenField.value :
22236                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22237     },
22238
22239     /**
22240      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22241      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22242      * (the default format used is "m/d/y").
22243      * <br />Usage:
22244      * <pre><code>
22245 //All of these calls set the same date value (May 4, 2006)
22246
22247 //Pass a date object:
22248 var dt = new Date('5/4/06');
22249 dateField.setValue(dt);
22250
22251 //Pass a date string (default format):
22252 dateField.setValue('5/4/06');
22253
22254 //Pass a date string (custom format):
22255 dateField.format = 'Y-m-d';
22256 dateField.setValue('2006-5-4');
22257 </code></pre>
22258      * @param {String/Date} date The date or valid date string
22259      */
22260     setValue : function(date){
22261         if (this.hiddenField) {
22262             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22263         }
22264         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22265         // make sure the value field is always stored as a date..
22266         this.value = this.parseDate(date);
22267         
22268         
22269     },
22270
22271     // private
22272     parseDate : function(value){
22273         if(!value || value instanceof Date){
22274             return value;
22275         }
22276         var v = Date.parseDate(value, this.format);
22277          if (!v && this.useIso) {
22278             v = Date.parseDate(value, 'Y-m-d');
22279         }
22280         if(!v && this.altFormats){
22281             if(!this.altFormatsArray){
22282                 this.altFormatsArray = this.altFormats.split("|");
22283             }
22284             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22285                 v = Date.parseDate(value, this.altFormatsArray[i]);
22286             }
22287         }
22288         return v;
22289     },
22290
22291     // private
22292     formatDate : function(date, fmt){
22293         return (!date || !(date instanceof Date)) ?
22294                date : date.dateFormat(fmt || this.format);
22295     },
22296
22297     // private
22298     menuListeners : {
22299         select: function(m, d){
22300             
22301             this.setValue(d);
22302             this.fireEvent('select', this, d);
22303         },
22304         show : function(){ // retain focus styling
22305             this.onFocus();
22306         },
22307         hide : function(){
22308             this.focus.defer(10, this);
22309             var ml = this.menuListeners;
22310             this.menu.un("select", ml.select,  this);
22311             this.menu.un("show", ml.show,  this);
22312             this.menu.un("hide", ml.hide,  this);
22313         }
22314     },
22315
22316     // private
22317     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22318     onTriggerClick : function(){
22319         if(this.disabled){
22320             return;
22321         }
22322         if(this.menu == null){
22323             this.menu = new Roo.menu.DateMenu();
22324         }
22325         Roo.apply(this.menu.picker,  {
22326             showClear: this.allowBlank,
22327             minDate : this.minValue,
22328             maxDate : this.maxValue,
22329             disabledDatesRE : this.ddMatch,
22330             disabledDatesText : this.disabledDatesText,
22331             disabledDays : this.disabledDays,
22332             disabledDaysText : this.disabledDaysText,
22333             format : this.useIso ? 'Y-m-d' : this.format,
22334             minText : String.format(this.minText, this.formatDate(this.minValue)),
22335             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22336         });
22337         this.menu.on(Roo.apply({}, this.menuListeners, {
22338             scope:this
22339         }));
22340         this.menu.picker.setValue(this.getValue() || new Date());
22341         this.menu.show(this.el, "tl-bl?");
22342     },
22343
22344     beforeBlur : function(){
22345         var v = this.parseDate(this.getRawValue());
22346         if(v){
22347             this.setValue(v);
22348         }
22349     },
22350
22351     /*@
22352      * overide
22353      * 
22354      */
22355     isDirty : function() {
22356         if(this.disabled) {
22357             return false;
22358         }
22359         
22360         if(typeof(this.startValue) === 'undefined'){
22361             return false;
22362         }
22363         
22364         return String(this.getValue()) !== String(this.startValue);
22365         
22366     }
22367 });/*
22368  * Based on:
22369  * Ext JS Library 1.1.1
22370  * Copyright(c) 2006-2007, Ext JS, LLC.
22371  *
22372  * Originally Released Under LGPL - original licence link has changed is not relivant.
22373  *
22374  * Fork - LGPL
22375  * <script type="text/javascript">
22376  */
22377  
22378 /**
22379  * @class Roo.form.MonthField
22380  * @extends Roo.form.TriggerField
22381  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22382 * @constructor
22383 * Create a new MonthField
22384 * @param {Object} config
22385  */
22386 Roo.form.MonthField = function(config){
22387     
22388     Roo.form.MonthField.superclass.constructor.call(this, config);
22389     
22390       this.addEvents({
22391          
22392         /**
22393          * @event select
22394          * Fires when a date is selected
22395              * @param {Roo.form.MonthFieeld} combo This combo box
22396              * @param {Date} date The date selected
22397              */
22398         'select' : true
22399          
22400     });
22401     
22402     
22403     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22404     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22405     this.ddMatch = null;
22406     if(this.disabledDates){
22407         var dd = this.disabledDates;
22408         var re = "(?:";
22409         for(var i = 0; i < dd.length; i++){
22410             re += dd[i];
22411             if(i != dd.length-1) re += "|";
22412         }
22413         this.ddMatch = new RegExp(re + ")");
22414     }
22415 };
22416
22417 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
22418     /**
22419      * @cfg {String} format
22420      * The default date format string which can be overriden for localization support.  The format must be
22421      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22422      */
22423     format : "M Y",
22424     /**
22425      * @cfg {String} altFormats
22426      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22427      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22428      */
22429     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
22430     /**
22431      * @cfg {Array} disabledDays
22432      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22433      */
22434     disabledDays : [0,1,2,3,4,5,6],
22435     /**
22436      * @cfg {String} disabledDaysText
22437      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22438      */
22439     disabledDaysText : "Disabled",
22440     /**
22441      * @cfg {Array} disabledDates
22442      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22443      * expression so they are very powerful. Some examples:
22444      * <ul>
22445      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22446      * <li>["03/08", "09/16"] would disable those days for every year</li>
22447      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22448      * <li>["03/../2006"] would disable every day in March 2006</li>
22449      * <li>["^03"] would disable every day in every March</li>
22450      * </ul>
22451      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22452      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22453      */
22454     disabledDates : null,
22455     /**
22456      * @cfg {String} disabledDatesText
22457      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22458      */
22459     disabledDatesText : "Disabled",
22460     /**
22461      * @cfg {Date/String} minValue
22462      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22463      * valid format (defaults to null).
22464      */
22465     minValue : null,
22466     /**
22467      * @cfg {Date/String} maxValue
22468      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22469      * valid format (defaults to null).
22470      */
22471     maxValue : null,
22472     /**
22473      * @cfg {String} minText
22474      * The error text to display when the date in the cell is before minValue (defaults to
22475      * 'The date in this field must be after {minValue}').
22476      */
22477     minText : "The date in this field must be equal to or after {0}",
22478     /**
22479      * @cfg {String} maxTextf
22480      * The error text to display when the date in the cell is after maxValue (defaults to
22481      * 'The date in this field must be before {maxValue}').
22482      */
22483     maxText : "The date in this field must be equal to or before {0}",
22484     /**
22485      * @cfg {String} invalidText
22486      * The error text to display when the date in the field is invalid (defaults to
22487      * '{value} is not a valid date - it must be in the format {format}').
22488      */
22489     invalidText : "{0} is not a valid date - it must be in the format {1}",
22490     /**
22491      * @cfg {String} triggerClass
22492      * An additional CSS class used to style the trigger button.  The trigger will always get the
22493      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22494      * which displays a calendar icon).
22495      */
22496     triggerClass : 'x-form-date-trigger',
22497     
22498
22499     /**
22500      * @cfg {Boolean} useIso
22501      * if enabled, then the date field will use a hidden field to store the 
22502      * real value as iso formated date. default (true)
22503      */ 
22504     useIso : true,
22505     /**
22506      * @cfg {String/Object} autoCreate
22507      * A DomHelper element spec, or true for a default element spec (defaults to
22508      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22509      */ 
22510     // private
22511     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22512     
22513     // private
22514     hiddenField: false,
22515     
22516     hideMonthPicker : false,
22517     
22518     onRender : function(ct, position)
22519     {
22520         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
22521         if (this.useIso) {
22522             this.el.dom.removeAttribute('name'); 
22523             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22524                     'before', true);
22525             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22526             // prevent input submission
22527             this.hiddenName = this.name;
22528         }
22529             
22530             
22531     },
22532     
22533     // private
22534     validateValue : function(value)
22535     {
22536         value = this.formatDate(value);
22537         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
22538             return false;
22539         }
22540         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22541              return true;
22542         }
22543         var svalue = value;
22544         value = this.parseDate(value);
22545         if(!value){
22546             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22547             return false;
22548         }
22549         var time = value.getTime();
22550         if(this.minValue && time < this.minValue.getTime()){
22551             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22552             return false;
22553         }
22554         if(this.maxValue && time > this.maxValue.getTime()){
22555             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22556             return false;
22557         }
22558         /*if(this.disabledDays){
22559             var day = value.getDay();
22560             for(var i = 0; i < this.disabledDays.length; i++) {
22561                 if(day === this.disabledDays[i]){
22562                     this.markInvalid(this.disabledDaysText);
22563                     return false;
22564                 }
22565             }
22566         }
22567         */
22568         var fvalue = this.formatDate(value);
22569         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
22570             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22571             return false;
22572         }
22573         */
22574         return true;
22575     },
22576
22577     // private
22578     // Provides logic to override the default TriggerField.validateBlur which just returns true
22579     validateBlur : function(){
22580         return !this.menu || !this.menu.isVisible();
22581     },
22582
22583     /**
22584      * Returns the current date value of the date field.
22585      * @return {Date} The date value
22586      */
22587     getValue : function(){
22588         
22589         
22590         
22591         return  this.hiddenField ?
22592                 this.hiddenField.value :
22593                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
22594     },
22595
22596     /**
22597      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22598      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
22599      * (the default format used is "m/d/y").
22600      * <br />Usage:
22601      * <pre><code>
22602 //All of these calls set the same date value (May 4, 2006)
22603
22604 //Pass a date object:
22605 var dt = new Date('5/4/06');
22606 monthField.setValue(dt);
22607
22608 //Pass a date string (default format):
22609 monthField.setValue('5/4/06');
22610
22611 //Pass a date string (custom format):
22612 monthField.format = 'Y-m-d';
22613 monthField.setValue('2006-5-4');
22614 </code></pre>
22615      * @param {String/Date} date The date or valid date string
22616      */
22617     setValue : function(date){
22618         Roo.log('month setValue' + date);
22619         // can only be first of month..
22620         
22621         var val = this.parseDate(date);
22622         
22623         if (this.hiddenField) {
22624             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22625         }
22626         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22627         this.value = this.parseDate(date);
22628     },
22629
22630     // private
22631     parseDate : function(value){
22632         if(!value || value instanceof Date){
22633             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
22634             return value;
22635         }
22636         var v = Date.parseDate(value, this.format);
22637         if (!v && this.useIso) {
22638             v = Date.parseDate(value, 'Y-m-d');
22639         }
22640         if (v) {
22641             // 
22642             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
22643         }
22644         
22645         
22646         if(!v && this.altFormats){
22647             if(!this.altFormatsArray){
22648                 this.altFormatsArray = this.altFormats.split("|");
22649             }
22650             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22651                 v = Date.parseDate(value, this.altFormatsArray[i]);
22652             }
22653         }
22654         return v;
22655     },
22656
22657     // private
22658     formatDate : function(date, fmt){
22659         return (!date || !(date instanceof Date)) ?
22660                date : date.dateFormat(fmt || this.format);
22661     },
22662
22663     // private
22664     menuListeners : {
22665         select: function(m, d){
22666             this.setValue(d);
22667             this.fireEvent('select', this, d);
22668         },
22669         show : function(){ // retain focus styling
22670             this.onFocus();
22671         },
22672         hide : function(){
22673             this.focus.defer(10, this);
22674             var ml = this.menuListeners;
22675             this.menu.un("select", ml.select,  this);
22676             this.menu.un("show", ml.show,  this);
22677             this.menu.un("hide", ml.hide,  this);
22678         }
22679     },
22680     // private
22681     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22682     onTriggerClick : function(){
22683         if(this.disabled){
22684             return;
22685         }
22686         if(this.menu == null){
22687             this.menu = new Roo.menu.DateMenu();
22688            
22689         }
22690         
22691         Roo.apply(this.menu.picker,  {
22692             
22693             showClear: this.allowBlank,
22694             minDate : this.minValue,
22695             maxDate : this.maxValue,
22696             disabledDatesRE : this.ddMatch,
22697             disabledDatesText : this.disabledDatesText,
22698             
22699             format : this.useIso ? 'Y-m-d' : this.format,
22700             minText : String.format(this.minText, this.formatDate(this.minValue)),
22701             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22702             
22703         });
22704          this.menu.on(Roo.apply({}, this.menuListeners, {
22705             scope:this
22706         }));
22707        
22708         
22709         var m = this.menu;
22710         var p = m.picker;
22711         
22712         // hide month picker get's called when we called by 'before hide';
22713         
22714         var ignorehide = true;
22715         p.hideMonthPicker  = function(disableAnim){
22716             if (ignorehide) {
22717                 return;
22718             }
22719              if(this.monthPicker){
22720                 Roo.log("hideMonthPicker called");
22721                 if(disableAnim === true){
22722                     this.monthPicker.hide();
22723                 }else{
22724                     this.monthPicker.slideOut('t', {duration:.2});
22725                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
22726                     p.fireEvent("select", this, this.value);
22727                     m.hide();
22728                 }
22729             }
22730         }
22731         
22732         Roo.log('picker set value');
22733         Roo.log(this.getValue());
22734         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
22735         m.show(this.el, 'tl-bl?');
22736         ignorehide  = false;
22737         // this will trigger hideMonthPicker..
22738         
22739         
22740         // hidden the day picker
22741         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
22742         
22743         
22744         
22745       
22746         
22747         p.showMonthPicker.defer(100, p);
22748     
22749         
22750        
22751     },
22752
22753     beforeBlur : function(){
22754         var v = this.parseDate(this.getRawValue());
22755         if(v){
22756             this.setValue(v);
22757         }
22758     }
22759
22760     /** @cfg {Boolean} grow @hide */
22761     /** @cfg {Number} growMin @hide */
22762     /** @cfg {Number} growMax @hide */
22763     /**
22764      * @hide
22765      * @method autoSize
22766      */
22767 });/*
22768  * Based on:
22769  * Ext JS Library 1.1.1
22770  * Copyright(c) 2006-2007, Ext JS, LLC.
22771  *
22772  * Originally Released Under LGPL - original licence link has changed is not relivant.
22773  *
22774  * Fork - LGPL
22775  * <script type="text/javascript">
22776  */
22777  
22778
22779 /**
22780  * @class Roo.form.ComboBox
22781  * @extends Roo.form.TriggerField
22782  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22783  * @constructor
22784  * Create a new ComboBox.
22785  * @param {Object} config Configuration options
22786  */
22787 Roo.form.ComboBox = function(config){
22788     Roo.form.ComboBox.superclass.constructor.call(this, config);
22789     this.addEvents({
22790         /**
22791          * @event expand
22792          * Fires when the dropdown list is expanded
22793              * @param {Roo.form.ComboBox} combo This combo box
22794              */
22795         'expand' : true,
22796         /**
22797          * @event collapse
22798          * Fires when the dropdown list is collapsed
22799              * @param {Roo.form.ComboBox} combo This combo box
22800              */
22801         'collapse' : true,
22802         /**
22803          * @event beforeselect
22804          * Fires before a list item is selected. Return false to cancel the selection.
22805              * @param {Roo.form.ComboBox} combo This combo box
22806              * @param {Roo.data.Record} record The data record returned from the underlying store
22807              * @param {Number} index The index of the selected item in the dropdown list
22808              */
22809         'beforeselect' : true,
22810         /**
22811          * @event select
22812          * Fires when a list item is selected
22813              * @param {Roo.form.ComboBox} combo This combo box
22814              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22815              * @param {Number} index The index of the selected item in the dropdown list
22816              */
22817         'select' : true,
22818         /**
22819          * @event beforequery
22820          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22821          * The event object passed has these properties:
22822              * @param {Roo.form.ComboBox} combo This combo box
22823              * @param {String} query The query
22824              * @param {Boolean} forceAll true to force "all" query
22825              * @param {Boolean} cancel true to cancel the query
22826              * @param {Object} e The query event object
22827              */
22828         'beforequery': true,
22829          /**
22830          * @event add
22831          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22832              * @param {Roo.form.ComboBox} combo This combo box
22833              */
22834         'add' : true,
22835         /**
22836          * @event edit
22837          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22838              * @param {Roo.form.ComboBox} combo This combo box
22839              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22840              */
22841         'edit' : true
22842         
22843         
22844     });
22845     if(this.transform){
22846         this.allowDomMove = false;
22847         var s = Roo.getDom(this.transform);
22848         if(!this.hiddenName){
22849             this.hiddenName = s.name;
22850         }
22851         if(!this.store){
22852             this.mode = 'local';
22853             var d = [], opts = s.options;
22854             for(var i = 0, len = opts.length;i < len; i++){
22855                 var o = opts[i];
22856                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22857                 if(o.selected) {
22858                     this.value = value;
22859                 }
22860                 d.push([value, o.text]);
22861             }
22862             this.store = new Roo.data.SimpleStore({
22863                 'id': 0,
22864                 fields: ['value', 'text'],
22865                 data : d
22866             });
22867             this.valueField = 'value';
22868             this.displayField = 'text';
22869         }
22870         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22871         if(!this.lazyRender){
22872             this.target = true;
22873             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22874             s.parentNode.removeChild(s); // remove it
22875             this.render(this.el.parentNode);
22876         }else{
22877             s.parentNode.removeChild(s); // remove it
22878         }
22879
22880     }
22881     if (this.store) {
22882         this.store = Roo.factory(this.store, Roo.data);
22883     }
22884     
22885     this.selectedIndex = -1;
22886     if(this.mode == 'local'){
22887         if(config.queryDelay === undefined){
22888             this.queryDelay = 10;
22889         }
22890         if(config.minChars === undefined){
22891             this.minChars = 0;
22892         }
22893     }
22894 };
22895
22896 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22897     /**
22898      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22899      */
22900     /**
22901      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22902      * rendering into an Roo.Editor, defaults to false)
22903      */
22904     /**
22905      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22906      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22907      */
22908     /**
22909      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22910      */
22911     /**
22912      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22913      * the dropdown list (defaults to undefined, with no header element)
22914      */
22915
22916      /**
22917      * @cfg {String/Roo.Template} tpl The template to use to render the output
22918      */
22919      
22920     // private
22921     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22922     /**
22923      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22924      */
22925     listWidth: undefined,
22926     /**
22927      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22928      * mode = 'remote' or 'text' if mode = 'local')
22929      */
22930     displayField: undefined,
22931     /**
22932      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22933      * mode = 'remote' or 'value' if mode = 'local'). 
22934      * Note: use of a valueField requires the user make a selection
22935      * in order for a value to be mapped.
22936      */
22937     valueField: undefined,
22938     
22939     
22940     /**
22941      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22942      * field's data value (defaults to the underlying DOM element's name)
22943      */
22944     hiddenName: undefined,
22945     /**
22946      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22947      */
22948     listClass: '',
22949     /**
22950      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22951      */
22952     selectedClass: 'x-combo-selected',
22953     /**
22954      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22955      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22956      * which displays a downward arrow icon).
22957      */
22958     triggerClass : 'x-form-arrow-trigger',
22959     /**
22960      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
22961      */
22962     shadow:'sides',
22963     /**
22964      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
22965      * anchor positions (defaults to 'tl-bl')
22966      */
22967     listAlign: 'tl-bl?',
22968     /**
22969      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
22970      */
22971     maxHeight: 300,
22972     /**
22973      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
22974      * query specified by the allQuery config option (defaults to 'query')
22975      */
22976     triggerAction: 'query',
22977     /**
22978      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
22979      * (defaults to 4, does not apply if editable = false)
22980      */
22981     minChars : 4,
22982     /**
22983      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
22984      * delay (typeAheadDelay) if it matches a known value (defaults to false)
22985      */
22986     typeAhead: false,
22987     /**
22988      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
22989      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
22990      */
22991     queryDelay: 500,
22992     /**
22993      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
22994      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
22995      */
22996     pageSize: 0,
22997     /**
22998      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
22999      * when editable = true (defaults to false)
23000      */
23001     selectOnFocus:false,
23002     /**
23003      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23004      */
23005     queryParam: 'query',
23006     /**
23007      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23008      * when mode = 'remote' (defaults to 'Loading...')
23009      */
23010     loadingText: 'Loading...',
23011     /**
23012      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23013      */
23014     resizable: false,
23015     /**
23016      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23017      */
23018     handleHeight : 8,
23019     /**
23020      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23021      * traditional select (defaults to true)
23022      */
23023     editable: true,
23024     /**
23025      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23026      */
23027     allQuery: '',
23028     /**
23029      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23030      */
23031     mode: 'remote',
23032     /**
23033      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23034      * listWidth has a higher value)
23035      */
23036     minListWidth : 70,
23037     /**
23038      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23039      * allow the user to set arbitrary text into the field (defaults to false)
23040      */
23041     forceSelection:false,
23042     /**
23043      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23044      * if typeAhead = true (defaults to 250)
23045      */
23046     typeAheadDelay : 250,
23047     /**
23048      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23049      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23050      */
23051     valueNotFoundText : undefined,
23052     /**
23053      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23054      */
23055     blockFocus : false,
23056     
23057     /**
23058      * @cfg {Boolean} disableClear Disable showing of clear button.
23059      */
23060     disableClear : false,
23061     /**
23062      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23063      */
23064     alwaysQuery : false,
23065     
23066     //private
23067     addicon : false,
23068     editicon: false,
23069     
23070     // element that contains real text value.. (when hidden is used..)
23071      
23072     // private
23073     onRender : function(ct, position){
23074         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23075         if(this.hiddenName){
23076             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23077                     'before', true);
23078             this.hiddenField.value =
23079                 this.hiddenValue !== undefined ? this.hiddenValue :
23080                 this.value !== undefined ? this.value : '';
23081
23082             // prevent input submission
23083             this.el.dom.removeAttribute('name');
23084              
23085              
23086         }
23087         if(Roo.isGecko){
23088             this.el.dom.setAttribute('autocomplete', 'off');
23089         }
23090
23091         var cls = 'x-combo-list';
23092
23093         this.list = new Roo.Layer({
23094             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23095         });
23096
23097         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23098         this.list.setWidth(lw);
23099         this.list.swallowEvent('mousewheel');
23100         this.assetHeight = 0;
23101
23102         if(this.title){
23103             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23104             this.assetHeight += this.header.getHeight();
23105         }
23106
23107         this.innerList = this.list.createChild({cls:cls+'-inner'});
23108         this.innerList.on('mouseover', this.onViewOver, this);
23109         this.innerList.on('mousemove', this.onViewMove, this);
23110         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23111         
23112         if(this.allowBlank && !this.pageSize && !this.disableClear){
23113             this.footer = this.list.createChild({cls:cls+'-ft'});
23114             this.pageTb = new Roo.Toolbar(this.footer);
23115            
23116         }
23117         if(this.pageSize){
23118             this.footer = this.list.createChild({cls:cls+'-ft'});
23119             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23120                     {pageSize: this.pageSize});
23121             
23122         }
23123         
23124         if (this.pageTb && this.allowBlank && !this.disableClear) {
23125             var _this = this;
23126             this.pageTb.add(new Roo.Toolbar.Fill(), {
23127                 cls: 'x-btn-icon x-btn-clear',
23128                 text: '&#160;',
23129                 handler: function()
23130                 {
23131                     _this.collapse();
23132                     _this.clearValue();
23133                     _this.onSelect(false, -1);
23134                 }
23135             });
23136         }
23137         if (this.footer) {
23138             this.assetHeight += this.footer.getHeight();
23139         }
23140         
23141
23142         if(!this.tpl){
23143             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23144         }
23145
23146         this.view = new Roo.View(this.innerList, this.tpl, {
23147             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23148         });
23149
23150         this.view.on('click', this.onViewClick, this);
23151
23152         this.store.on('beforeload', this.onBeforeLoad, this);
23153         this.store.on('load', this.onLoad, this);
23154         this.store.on('loadexception', this.onLoadException, this);
23155
23156         if(this.resizable){
23157             this.resizer = new Roo.Resizable(this.list,  {
23158                pinned:true, handles:'se'
23159             });
23160             this.resizer.on('resize', function(r, w, h){
23161                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23162                 this.listWidth = w;
23163                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23164                 this.restrictHeight();
23165             }, this);
23166             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23167         }
23168         if(!this.editable){
23169             this.editable = true;
23170             this.setEditable(false);
23171         }  
23172         
23173         
23174         if (typeof(this.events.add.listeners) != 'undefined') {
23175             
23176             this.addicon = this.wrap.createChild(
23177                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23178        
23179             this.addicon.on('click', function(e) {
23180                 this.fireEvent('add', this);
23181             }, this);
23182         }
23183         if (typeof(this.events.edit.listeners) != 'undefined') {
23184             
23185             this.editicon = this.wrap.createChild(
23186                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23187             if (this.addicon) {
23188                 this.editicon.setStyle('margin-left', '40px');
23189             }
23190             this.editicon.on('click', function(e) {
23191                 
23192                 // we fire even  if inothing is selected..
23193                 this.fireEvent('edit', this, this.lastData );
23194                 
23195             }, this);
23196         }
23197         
23198         
23199         
23200     },
23201
23202     // private
23203     initEvents : function(){
23204         Roo.form.ComboBox.superclass.initEvents.call(this);
23205
23206         this.keyNav = new Roo.KeyNav(this.el, {
23207             "up" : function(e){
23208                 this.inKeyMode = true;
23209                 this.selectPrev();
23210             },
23211
23212             "down" : function(e){
23213                 if(!this.isExpanded()){
23214                     this.onTriggerClick();
23215                 }else{
23216                     this.inKeyMode = true;
23217                     this.selectNext();
23218                 }
23219             },
23220
23221             "enter" : function(e){
23222                 this.onViewClick();
23223                 //return true;
23224             },
23225
23226             "esc" : function(e){
23227                 this.collapse();
23228             },
23229
23230             "tab" : function(e){
23231                 this.onViewClick(false);
23232                 this.fireEvent("specialkey", this, e);
23233                 return true;
23234             },
23235
23236             scope : this,
23237
23238             doRelay : function(foo, bar, hname){
23239                 if(hname == 'down' || this.scope.isExpanded()){
23240                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23241                 }
23242                 return true;
23243             },
23244
23245             forceKeyDown: true
23246         });
23247         this.queryDelay = Math.max(this.queryDelay || 10,
23248                 this.mode == 'local' ? 10 : 250);
23249         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23250         if(this.typeAhead){
23251             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23252         }
23253         if(this.editable !== false){
23254             this.el.on("keyup", this.onKeyUp, this);
23255         }
23256         if(this.forceSelection){
23257             this.on('blur', this.doForce, this);
23258         }
23259     },
23260
23261     onDestroy : function(){
23262         if(this.view){
23263             this.view.setStore(null);
23264             this.view.el.removeAllListeners();
23265             this.view.el.remove();
23266             this.view.purgeListeners();
23267         }
23268         if(this.list){
23269             this.list.destroy();
23270         }
23271         if(this.store){
23272             this.store.un('beforeload', this.onBeforeLoad, this);
23273             this.store.un('load', this.onLoad, this);
23274             this.store.un('loadexception', this.onLoadException, this);
23275         }
23276         Roo.form.ComboBox.superclass.onDestroy.call(this);
23277     },
23278
23279     // private
23280     fireKey : function(e){
23281         if(e.isNavKeyPress() && !this.list.isVisible()){
23282             this.fireEvent("specialkey", this, e);
23283         }
23284     },
23285
23286     // private
23287     onResize: function(w, h){
23288         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23289         
23290         if(typeof w != 'number'){
23291             // we do not handle it!?!?
23292             return;
23293         }
23294         var tw = this.trigger.getWidth();
23295         tw += this.addicon ? this.addicon.getWidth() : 0;
23296         tw += this.editicon ? this.editicon.getWidth() : 0;
23297         var x = w - tw;
23298         this.el.setWidth( this.adjustWidth('input', x));
23299             
23300         this.trigger.setStyle('left', x+'px');
23301         
23302         if(this.list && this.listWidth === undefined){
23303             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23304             this.list.setWidth(lw);
23305             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23306         }
23307         
23308     
23309         
23310     },
23311
23312     /**
23313      * Allow or prevent the user from directly editing the field text.  If false is passed,
23314      * the user will only be able to select from the items defined in the dropdown list.  This method
23315      * is the runtime equivalent of setting the 'editable' config option at config time.
23316      * @param {Boolean} value True to allow the user to directly edit the field text
23317      */
23318     setEditable : function(value){
23319         if(value == this.editable){
23320             return;
23321         }
23322         this.editable = value;
23323         if(!value){
23324             this.el.dom.setAttribute('readOnly', true);
23325             this.el.on('mousedown', this.onTriggerClick,  this);
23326             this.el.addClass('x-combo-noedit');
23327         }else{
23328             this.el.dom.setAttribute('readOnly', false);
23329             this.el.un('mousedown', this.onTriggerClick,  this);
23330             this.el.removeClass('x-combo-noedit');
23331         }
23332     },
23333
23334     // private
23335     onBeforeLoad : function(){
23336         if(!this.hasFocus){
23337             return;
23338         }
23339         this.innerList.update(this.loadingText ?
23340                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23341         this.restrictHeight();
23342         this.selectedIndex = -1;
23343     },
23344
23345     // private
23346     onLoad : function(){
23347         if(!this.hasFocus){
23348             return;
23349         }
23350         if(this.store.getCount() > 0){
23351             this.expand();
23352             this.restrictHeight();
23353             if(this.lastQuery == this.allQuery){
23354                 if(this.editable){
23355                     this.el.dom.select();
23356                 }
23357                 if(!this.selectByValue(this.value, true)){
23358                     this.select(0, true);
23359                 }
23360             }else{
23361                 this.selectNext();
23362                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23363                     this.taTask.delay(this.typeAheadDelay);
23364                 }
23365             }
23366         }else{
23367             this.onEmptyResults();
23368         }
23369         //this.el.focus();
23370     },
23371     // private
23372     onLoadException : function()
23373     {
23374         this.collapse();
23375         Roo.log(this.store.reader.jsonData);
23376         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
23377             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
23378         }
23379         
23380         
23381     },
23382     // private
23383     onTypeAhead : function(){
23384         if(this.store.getCount() > 0){
23385             var r = this.store.getAt(0);
23386             var newValue = r.data[this.displayField];
23387             var len = newValue.length;
23388             var selStart = this.getRawValue().length;
23389             if(selStart != len){
23390                 this.setRawValue(newValue);
23391                 this.selectText(selStart, newValue.length);
23392             }
23393         }
23394     },
23395
23396     // private
23397     onSelect : function(record, index){
23398         if(this.fireEvent('beforeselect', this, record, index) !== false){
23399             this.setFromData(index > -1 ? record.data : false);
23400             this.collapse();
23401             this.fireEvent('select', this, record, index);
23402         }
23403     },
23404
23405     /**
23406      * Returns the currently selected field value or empty string if no value is set.
23407      * @return {String} value The selected value
23408      */
23409     getValue : function(){
23410         if(this.valueField){
23411             return typeof this.value != 'undefined' ? this.value : '';
23412         }
23413         return Roo.form.ComboBox.superclass.getValue.call(this);
23414     },
23415
23416     /**
23417      * Clears any text/value currently set in the field
23418      */
23419     clearValue : function(){
23420         if(this.hiddenField){
23421             this.hiddenField.value = '';
23422         }
23423         this.value = '';
23424         this.setRawValue('');
23425         this.lastSelectionText = '';
23426         
23427     },
23428
23429     /**
23430      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23431      * will be displayed in the field.  If the value does not match the data value of an existing item,
23432      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23433      * Otherwise the field will be blank (although the value will still be set).
23434      * @param {String} value The value to match
23435      */
23436     setValue : function(v){
23437         var text = v;
23438         if(this.valueField){
23439             var r = this.findRecord(this.valueField, v);
23440             if(r){
23441                 text = r.data[this.displayField];
23442             }else if(this.valueNotFoundText !== undefined){
23443                 text = this.valueNotFoundText;
23444             }
23445         }
23446         this.lastSelectionText = text;
23447         if(this.hiddenField){
23448             this.hiddenField.value = v;
23449         }
23450         Roo.form.ComboBox.superclass.setValue.call(this, text);
23451         this.value = v;
23452     },
23453     /**
23454      * @property {Object} the last set data for the element
23455      */
23456     
23457     lastData : false,
23458     /**
23459      * Sets the value of the field based on a object which is related to the record format for the store.
23460      * @param {Object} value the value to set as. or false on reset?
23461      */
23462     setFromData : function(o){
23463         var dv = ''; // display value
23464         var vv = ''; // value value..
23465         this.lastData = o;
23466         if (this.displayField) {
23467             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23468         } else {
23469             // this is an error condition!!!
23470             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23471         }
23472         
23473         if(this.valueField){
23474             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23475         }
23476         if(this.hiddenField){
23477             this.hiddenField.value = vv;
23478             
23479             this.lastSelectionText = dv;
23480             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23481             this.value = vv;
23482             return;
23483         }
23484         // no hidden field.. - we store the value in 'value', but still display
23485         // display field!!!!
23486         this.lastSelectionText = dv;
23487         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23488         this.value = vv;
23489         
23490         
23491     },
23492     // private
23493     reset : function(){
23494         // overridden so that last data is reset..
23495         this.setValue(this.resetValue);
23496         this.clearInvalid();
23497         this.lastData = false;
23498         if (this.view) {
23499             this.view.clearSelections();
23500         }
23501     },
23502     // private
23503     findRecord : function(prop, value){
23504         var record;
23505         if(this.store.getCount() > 0){
23506             this.store.each(function(r){
23507                 if(r.data[prop] == value){
23508                     record = r;
23509                     return false;
23510                 }
23511                 return true;
23512             });
23513         }
23514         return record;
23515     },
23516     
23517     getName: function()
23518     {
23519         // returns hidden if it's set..
23520         if (!this.rendered) {return ''};
23521         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
23522         
23523     },
23524     // private
23525     onViewMove : function(e, t){
23526         this.inKeyMode = false;
23527     },
23528
23529     // private
23530     onViewOver : function(e, t){
23531         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23532             return;
23533         }
23534         var item = this.view.findItemFromChild(t);
23535         if(item){
23536             var index = this.view.indexOf(item);
23537             this.select(index, false);
23538         }
23539     },
23540
23541     // private
23542     onViewClick : function(doFocus)
23543     {
23544         var index = this.view.getSelectedIndexes()[0];
23545         var r = this.store.getAt(index);
23546         if(r){
23547             this.onSelect(r, index);
23548         }
23549         if(doFocus !== false && !this.blockFocus){
23550             this.el.focus();
23551         }
23552     },
23553
23554     // private
23555     restrictHeight : function(){
23556         this.innerList.dom.style.height = '';
23557         var inner = this.innerList.dom;
23558         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23559         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23560         this.list.beginUpdate();
23561         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23562         this.list.alignTo(this.el, this.listAlign);
23563         this.list.endUpdate();
23564     },
23565
23566     // private
23567     onEmptyResults : function(){
23568         this.collapse();
23569     },
23570
23571     /**
23572      * Returns true if the dropdown list is expanded, else false.
23573      */
23574     isExpanded : function(){
23575         return this.list.isVisible();
23576     },
23577
23578     /**
23579      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23580      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23581      * @param {String} value The data value of the item to select
23582      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23583      * selected item if it is not currently in view (defaults to true)
23584      * @return {Boolean} True if the value matched an item in the list, else false
23585      */
23586     selectByValue : function(v, scrollIntoView){
23587         if(v !== undefined && v !== null){
23588             var r = this.findRecord(this.valueField || this.displayField, v);
23589             if(r){
23590                 this.select(this.store.indexOf(r), scrollIntoView);
23591                 return true;
23592             }
23593         }
23594         return false;
23595     },
23596
23597     /**
23598      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23599      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23600      * @param {Number} index The zero-based index of the list item to select
23601      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23602      * selected item if it is not currently in view (defaults to true)
23603      */
23604     select : function(index, scrollIntoView){
23605         this.selectedIndex = index;
23606         this.view.select(index);
23607         if(scrollIntoView !== false){
23608             var el = this.view.getNode(index);
23609             if(el){
23610                 this.innerList.scrollChildIntoView(el, false);
23611             }
23612         }
23613     },
23614
23615     // private
23616     selectNext : function(){
23617         var ct = this.store.getCount();
23618         if(ct > 0){
23619             if(this.selectedIndex == -1){
23620                 this.select(0);
23621             }else if(this.selectedIndex < ct-1){
23622                 this.select(this.selectedIndex+1);
23623             }
23624         }
23625     },
23626
23627     // private
23628     selectPrev : function(){
23629         var ct = this.store.getCount();
23630         if(ct > 0){
23631             if(this.selectedIndex == -1){
23632                 this.select(0);
23633             }else if(this.selectedIndex != 0){
23634                 this.select(this.selectedIndex-1);
23635             }
23636         }
23637     },
23638
23639     // private
23640     onKeyUp : function(e){
23641         if(this.editable !== false && !e.isSpecialKey()){
23642             this.lastKey = e.getKey();
23643             this.dqTask.delay(this.queryDelay);
23644         }
23645     },
23646
23647     // private
23648     validateBlur : function(){
23649         return !this.list || !this.list.isVisible();   
23650     },
23651
23652     // private
23653     initQuery : function(){
23654         this.doQuery(this.getRawValue());
23655     },
23656
23657     // private
23658     doForce : function(){
23659         if(this.el.dom.value.length > 0){
23660             this.el.dom.value =
23661                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23662              
23663         }
23664     },
23665
23666     /**
23667      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23668      * query allowing the query action to be canceled if needed.
23669      * @param {String} query The SQL query to execute
23670      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23671      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23672      * saved in the current store (defaults to false)
23673      */
23674     doQuery : function(q, forceAll){
23675         if(q === undefined || q === null){
23676             q = '';
23677         }
23678         var qe = {
23679             query: q,
23680             forceAll: forceAll,
23681             combo: this,
23682             cancel:false
23683         };
23684         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23685             return false;
23686         }
23687         q = qe.query;
23688         forceAll = qe.forceAll;
23689         if(forceAll === true || (q.length >= this.minChars)){
23690             if(this.lastQuery != q || this.alwaysQuery){
23691                 this.lastQuery = q;
23692                 if(this.mode == 'local'){
23693                     this.selectedIndex = -1;
23694                     if(forceAll){
23695                         this.store.clearFilter();
23696                     }else{
23697                         this.store.filter(this.displayField, q);
23698                     }
23699                     this.onLoad();
23700                 }else{
23701                     this.store.baseParams[this.queryParam] = q;
23702                     this.store.load({
23703                         params: this.getParams(q)
23704                     });
23705                     this.expand();
23706                 }
23707             }else{
23708                 this.selectedIndex = -1;
23709                 this.onLoad();   
23710             }
23711         }
23712     },
23713
23714     // private
23715     getParams : function(q){
23716         var p = {};
23717         //p[this.queryParam] = q;
23718         if(this.pageSize){
23719             p.start = 0;
23720             p.limit = this.pageSize;
23721         }
23722         return p;
23723     },
23724
23725     /**
23726      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23727      */
23728     collapse : function(){
23729         if(!this.isExpanded()){
23730             return;
23731         }
23732         this.list.hide();
23733         Roo.get(document).un('mousedown', this.collapseIf, this);
23734         Roo.get(document).un('mousewheel', this.collapseIf, this);
23735         if (!this.editable) {
23736             Roo.get(document).un('keydown', this.listKeyPress, this);
23737         }
23738         this.fireEvent('collapse', this);
23739     },
23740
23741     // private
23742     collapseIf : function(e){
23743         if(!e.within(this.wrap) && !e.within(this.list)){
23744             this.collapse();
23745         }
23746     },
23747
23748     /**
23749      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23750      */
23751     expand : function(){
23752         if(this.isExpanded() || !this.hasFocus){
23753             return;
23754         }
23755         this.list.alignTo(this.el, this.listAlign);
23756         this.list.show();
23757         Roo.get(document).on('mousedown', this.collapseIf, this);
23758         Roo.get(document).on('mousewheel', this.collapseIf, this);
23759         if (!this.editable) {
23760             Roo.get(document).on('keydown', this.listKeyPress, this);
23761         }
23762         
23763         this.fireEvent('expand', this);
23764     },
23765
23766     // private
23767     // Implements the default empty TriggerField.onTriggerClick function
23768     onTriggerClick : function(){
23769         if(this.disabled){
23770             return;
23771         }
23772         if(this.isExpanded()){
23773             this.collapse();
23774             if (!this.blockFocus) {
23775                 this.el.focus();
23776             }
23777             
23778         }else {
23779             this.hasFocus = true;
23780             if(this.triggerAction == 'all') {
23781                 this.doQuery(this.allQuery, true);
23782             } else {
23783                 this.doQuery(this.getRawValue());
23784             }
23785             if (!this.blockFocus) {
23786                 this.el.focus();
23787             }
23788         }
23789     },
23790     listKeyPress : function(e)
23791     {
23792         //Roo.log('listkeypress');
23793         // scroll to first matching element based on key pres..
23794         if (e.isSpecialKey()) {
23795             return false;
23796         }
23797         var k = String.fromCharCode(e.getKey()).toUpperCase();
23798         //Roo.log(k);
23799         var match  = false;
23800         var csel = this.view.getSelectedNodes();
23801         var cselitem = false;
23802         if (csel.length) {
23803             var ix = this.view.indexOf(csel[0]);
23804             cselitem  = this.store.getAt(ix);
23805             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23806                 cselitem = false;
23807             }
23808             
23809         }
23810         
23811         this.store.each(function(v) { 
23812             if (cselitem) {
23813                 // start at existing selection.
23814                 if (cselitem.id == v.id) {
23815                     cselitem = false;
23816                 }
23817                 return;
23818             }
23819                 
23820             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23821                 match = this.store.indexOf(v);
23822                 return false;
23823             }
23824         }, this);
23825         
23826         if (match === false) {
23827             return true; // no more action?
23828         }
23829         // scroll to?
23830         this.view.select(match);
23831         var sn = Roo.get(this.view.getSelectedNodes()[0])
23832         sn.scrollIntoView(sn.dom.parentNode, false);
23833     }
23834
23835     /** 
23836     * @cfg {Boolean} grow 
23837     * @hide 
23838     */
23839     /** 
23840     * @cfg {Number} growMin 
23841     * @hide 
23842     */
23843     /** 
23844     * @cfg {Number} growMax 
23845     * @hide 
23846     */
23847     /**
23848      * @hide
23849      * @method autoSize
23850      */
23851 });/*
23852  * Copyright(c) 2010-2012, Roo J Solutions Limited
23853  *
23854  * Licence LGPL
23855  *
23856  */
23857
23858 /**
23859  * @class Roo.form.ComboBoxArray
23860  * @extends Roo.form.TextField
23861  * A facebook style adder... for lists of email / people / countries  etc...
23862  * pick multiple items from a combo box, and shows each one.
23863  *
23864  *  Fred [x]  Brian [x]  [Pick another |v]
23865  *
23866  *
23867  *  For this to work: it needs various extra information
23868  *    - normal combo problay has
23869  *      name, hiddenName
23870  *    + displayField, valueField
23871  *
23872  *    For our purpose...
23873  *
23874  *
23875  *   If we change from 'extends' to wrapping...
23876  *   
23877  *  
23878  *
23879  
23880  
23881  * @constructor
23882  * Create a new ComboBoxArray.
23883  * @param {Object} config Configuration options
23884  */
23885  
23886
23887 Roo.form.ComboBoxArray = function(config)
23888 {
23889     this.addEvents({
23890         /**
23891          * @event beforeremove
23892          * Fires before remove the value from the list
23893              * @param {Roo.form.ComboBoxArray} _self This combo box array
23894              * @param {Roo.form.ComboBoxArray.Item} item removed item
23895              */
23896         'beforeremove' : true,
23897         /**
23898          * @event remove
23899          * Fires when remove the value from the list
23900              * @param {Roo.form.ComboBoxArray} _self This combo box array
23901              * @param {Roo.form.ComboBoxArray.Item} item removed item
23902              */
23903         'remove' : true
23904         
23905         
23906     });
23907     
23908     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
23909     
23910     this.items = new Roo.util.MixedCollection(false);
23911     
23912     // construct the child combo...
23913     
23914     
23915     
23916     
23917    
23918     
23919 }
23920
23921  
23922 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
23923
23924     /**
23925      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
23926      */
23927     
23928     lastData : false,
23929     
23930     // behavies liek a hiddne field
23931     inputType:      'hidden',
23932     /**
23933      * @cfg {Number} width The width of the box that displays the selected element
23934      */ 
23935     width:          300,
23936
23937     
23938     
23939     /**
23940      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
23941      */
23942     name : false,
23943     /**
23944      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
23945      */
23946     hiddenName : false,
23947     
23948     
23949     // private the array of items that are displayed..
23950     items  : false,
23951     // private - the hidden field el.
23952     hiddenEl : false,
23953     // private - the filed el..
23954     el : false,
23955     
23956     //validateValue : function() { return true; }, // all values are ok!
23957     //onAddClick: function() { },
23958     
23959     onRender : function(ct, position) 
23960     {
23961         
23962         // create the standard hidden element
23963         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
23964         
23965         
23966         // give fake names to child combo;
23967         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
23968         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
23969         
23970         this.combo = Roo.factory(this.combo, Roo.form);
23971         this.combo.onRender(ct, position);
23972         if (typeof(this.combo.width) != 'undefined') {
23973             this.combo.onResize(this.combo.width,0);
23974         }
23975         
23976         this.combo.initEvents();
23977         
23978         // assigned so form know we need to do this..
23979         this.store          = this.combo.store;
23980         this.valueField     = this.combo.valueField;
23981         this.displayField   = this.combo.displayField ;
23982         
23983         
23984         this.combo.wrap.addClass('x-cbarray-grp');
23985         
23986         var cbwrap = this.combo.wrap.createChild(
23987             {tag: 'div', cls: 'x-cbarray-cb'},
23988             this.combo.el.dom
23989         );
23990         
23991              
23992         this.hiddenEl = this.combo.wrap.createChild({
23993             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
23994         });
23995         this.el = this.combo.wrap.createChild({
23996             tag: 'input',  type:'hidden' , name: this.name, value : ''
23997         });
23998          //   this.el.dom.removeAttribute("name");
23999         
24000         
24001         this.outerWrap = this.combo.wrap;
24002         this.wrap = cbwrap;
24003         
24004         this.outerWrap.setWidth(this.width);
24005         this.outerWrap.dom.removeChild(this.el.dom);
24006         
24007         this.wrap.dom.appendChild(this.el.dom);
24008         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
24009         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
24010         
24011         this.combo.trigger.setStyle('position','relative');
24012         this.combo.trigger.setStyle('left', '0px');
24013         this.combo.trigger.setStyle('top', '2px');
24014         
24015         this.combo.el.setStyle('vertical-align', 'text-bottom');
24016         
24017         //this.trigger.setStyle('vertical-align', 'top');
24018         
24019         // this should use the code from combo really... on('add' ....)
24020         if (this.adder) {
24021             
24022         
24023             this.adder = this.outerWrap.createChild(
24024                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
24025             var _t = this;
24026             this.adder.on('click', function(e) {
24027                 _t.fireEvent('adderclick', this, e);
24028             }, _t);
24029         }
24030         //var _t = this;
24031         //this.adder.on('click', this.onAddClick, _t);
24032         
24033         
24034         this.combo.on('select', function(cb, rec, ix) {
24035             this.addItem(rec.data);
24036             
24037             cb.setValue('');
24038             cb.el.dom.value = '';
24039             //cb.lastData = rec.data;
24040             // add to list
24041             
24042         }, this);
24043         
24044         
24045     },
24046     
24047     
24048     getName: function()
24049     {
24050         // returns hidden if it's set..
24051         if (!this.rendered) {return ''};
24052         return  this.hiddenName ? this.hiddenName : this.name;
24053         
24054     },
24055     
24056     
24057     onResize: function(w, h){
24058         
24059         return;
24060         // not sure if this is needed..
24061         //this.combo.onResize(w,h);
24062         
24063         if(typeof w != 'number'){
24064             // we do not handle it!?!?
24065             return;
24066         }
24067         var tw = this.combo.trigger.getWidth();
24068         tw += this.addicon ? this.addicon.getWidth() : 0;
24069         tw += this.editicon ? this.editicon.getWidth() : 0;
24070         var x = w - tw;
24071         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24072             
24073         this.combo.trigger.setStyle('left', '0px');
24074         
24075         if(this.list && this.listWidth === undefined){
24076             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24077             this.list.setWidth(lw);
24078             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24079         }
24080         
24081     
24082         
24083     },
24084     
24085     addItem: function(rec)
24086     {
24087         var valueField = this.combo.valueField;
24088         var displayField = this.combo.displayField;
24089         if (this.items.indexOfKey(rec[valueField]) > -1) {
24090             //console.log("GOT " + rec.data.id);
24091             return;
24092         }
24093         
24094         var x = new Roo.form.ComboBoxArray.Item({
24095             //id : rec[this.idField],
24096             data : rec,
24097             displayField : displayField ,
24098             tipField : displayField ,
24099             cb : this
24100         });
24101         // use the 
24102         this.items.add(rec[valueField],x);
24103         // add it before the element..
24104         this.updateHiddenEl();
24105         x.render(this.outerWrap, this.wrap.dom);
24106         // add the image handler..
24107     },
24108     
24109     updateHiddenEl : function()
24110     {
24111         this.validate();
24112         if (!this.hiddenEl) {
24113             return;
24114         }
24115         var ar = [];
24116         var idField = this.combo.valueField;
24117         
24118         this.items.each(function(f) {
24119             ar.push(f.data[idField]);
24120            
24121         });
24122         this.hiddenEl.dom.value = ar.join(',');
24123         this.validate();
24124     },
24125     
24126     reset : function()
24127     {
24128         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
24129         this.items.each(function(f) {
24130            f.remove(); 
24131         });
24132         this.el.dom.value = '';
24133         if (this.hiddenEl) {
24134             this.hiddenEl.dom.value = '';
24135         }
24136         
24137     },
24138     getValue: function()
24139     {
24140         return this.hiddenEl ? this.hiddenEl.dom.value : '';
24141     },
24142     setValue: function(v) // not a valid action - must use addItems..
24143     {
24144          
24145         this.reset();
24146         
24147         
24148         
24149         if (this.store.isLocal && (typeof(v) == 'string')) {
24150             // then we can use the store to find the values..
24151             // comma seperated at present.. this needs to allow JSON based encoding..
24152             this.hiddenEl.value  = v;
24153             var v_ar = [];
24154             Roo.each(v.split(','), function(k) {
24155                 Roo.log("CHECK " + this.valueField + ',' + k);
24156                 var li = this.store.query(this.valueField, k);
24157                 if (!li.length) {
24158                     return;
24159                 }
24160                 var add = {};
24161                 add[this.valueField] = k;
24162                 add[this.displayField] = li.item(0).data[this.displayField];
24163                 
24164                 this.addItem(add);
24165             }, this) 
24166              
24167         }
24168         if (typeof(v) == 'object' ) {
24169             // then let's assume it's an array of objects..
24170             Roo.each(v, function(l) {
24171                 this.addItem(l);
24172             }, this);
24173              
24174         }
24175         
24176         
24177     },
24178     setFromData: function(v)
24179     {
24180         // this recieves an object, if setValues is called.
24181         this.reset();
24182         this.el.dom.value = v[this.displayField];
24183         this.hiddenEl.dom.value = v[this.valueField];
24184         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24185             return;
24186         }
24187         var kv = v[this.valueField];
24188         var dv = v[this.displayField];
24189         kv = typeof(kv) != 'string' ? '' : kv;
24190         dv = typeof(dv) != 'string' ? '' : dv;
24191         
24192         
24193         var keys = kv.split(',');
24194         var display = dv.split(',');
24195         for (var i = 0 ; i < keys.length; i++) {
24196             
24197             add = {};
24198             add[this.valueField] = keys[i];
24199             add[this.displayField] = display[i];
24200             this.addItem(add);
24201         }
24202       
24203         
24204     },
24205     
24206     /**
24207      * Validates the combox array value
24208      * @return {Boolean} True if the value is valid, else false
24209      */
24210     validate : function(){
24211         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
24212             this.clearInvalid();
24213             return true;
24214         }
24215         return false;
24216     },
24217     
24218     validateValue : function(value){
24219         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24220         
24221     },
24222     
24223     /*@
24224      * overide
24225      * 
24226      */
24227     isDirty : function() {
24228         if(this.disabled) {
24229             return false;
24230         }
24231         
24232         try {
24233             var d = Roo.decode(String(this.originalValue));
24234         } catch (e) {
24235             return String(this.getValue()) !== String(this.originalValue);
24236         }
24237         
24238         var originalValue = [];
24239         
24240         for (var i = 0; i < d.length; i++){
24241             originalValue.push(d[i][this.valueField]);
24242         }
24243         
24244         return String(this.getValue()) !== String(originalValue.join(','));
24245         
24246     }
24247     
24248 });
24249
24250
24251
24252 /**
24253  * @class Roo.form.ComboBoxArray.Item
24254  * @extends Roo.BoxComponent
24255  * A selected item in the list
24256  *  Fred [x]  Brian [x]  [Pick another |v]
24257  * 
24258  * @constructor
24259  * Create a new item.
24260  * @param {Object} config Configuration options
24261  */
24262  
24263 Roo.form.ComboBoxArray.Item = function(config) {
24264     config.id = Roo.id();
24265     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24266 }
24267
24268 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24269     data : {},
24270     cb: false,
24271     displayField : false,
24272     tipField : false,
24273     
24274     
24275     defaultAutoCreate : {
24276         tag: 'div',
24277         cls: 'x-cbarray-item',
24278         cn : [ 
24279             { tag: 'div' },
24280             {
24281                 tag: 'img',
24282                 width:16,
24283                 height : 16,
24284                 src : Roo.BLANK_IMAGE_URL ,
24285                 align: 'center'
24286             }
24287         ]
24288         
24289     },
24290     
24291  
24292     onRender : function(ct, position)
24293     {
24294         Roo.form.Field.superclass.onRender.call(this, ct, position);
24295         
24296         if(!this.el){
24297             var cfg = this.getAutoCreate();
24298             this.el = ct.createChild(cfg, position);
24299         }
24300         
24301         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24302         
24303         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
24304             this.cb.renderer(this.data) :
24305             String.format('{0}',this.data[this.displayField]);
24306         
24307             
24308         this.el.child('div').dom.setAttribute('qtip',
24309                         String.format('{0}',this.data[this.tipField])
24310         );
24311         
24312         this.el.child('img').on('click', this.remove, this);
24313         
24314     },
24315    
24316     remove : function()
24317     {
24318         if(this.cb.disabled){
24319             return;
24320         }
24321         
24322         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
24323             this.cb.items.remove(this);
24324             this.el.child('img').un('click', this.remove, this);
24325             this.el.remove();
24326             this.cb.updateHiddenEl();
24327
24328             this.cb.fireEvent('remove', this.cb, this);
24329         }
24330         
24331     }
24332 });/*
24333  * Based on:
24334  * Ext JS Library 1.1.1
24335  * Copyright(c) 2006-2007, Ext JS, LLC.
24336  *
24337  * Originally Released Under LGPL - original licence link has changed is not relivant.
24338  *
24339  * Fork - LGPL
24340  * <script type="text/javascript">
24341  */
24342 /**
24343  * @class Roo.form.Checkbox
24344  * @extends Roo.form.Field
24345  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
24346  * @constructor
24347  * Creates a new Checkbox
24348  * @param {Object} config Configuration options
24349  */
24350 Roo.form.Checkbox = function(config){
24351     Roo.form.Checkbox.superclass.constructor.call(this, config);
24352     this.addEvents({
24353         /**
24354          * @event check
24355          * Fires when the checkbox is checked or unchecked.
24356              * @param {Roo.form.Checkbox} this This checkbox
24357              * @param {Boolean} checked The new checked value
24358              */
24359         check : true
24360     });
24361 };
24362
24363 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
24364     /**
24365      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24366      */
24367     focusClass : undefined,
24368     /**
24369      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24370      */
24371     fieldClass: "x-form-field",
24372     /**
24373      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24374      */
24375     checked: false,
24376     /**
24377      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24378      * {tag: "input", type: "checkbox", autocomplete: "off"})
24379      */
24380     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
24381     /**
24382      * @cfg {String} boxLabel The text that appears beside the checkbox
24383      */
24384     boxLabel : "",
24385     /**
24386      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
24387      */  
24388     inputValue : '1',
24389     /**
24390      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24391      */
24392      valueOff: '0', // value when not checked..
24393
24394     actionMode : 'viewEl', 
24395     //
24396     // private
24397     itemCls : 'x-menu-check-item x-form-item',
24398     groupClass : 'x-menu-group-item',
24399     inputType : 'hidden',
24400     
24401     
24402     inSetChecked: false, // check that we are not calling self...
24403     
24404     inputElement: false, // real input element?
24405     basedOn: false, // ????
24406     
24407     isFormField: true, // not sure where this is needed!!!!
24408
24409     onResize : function(){
24410         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
24411         if(!this.boxLabel){
24412             this.el.alignTo(this.wrap, 'c-c');
24413         }
24414     },
24415
24416     initEvents : function(){
24417         Roo.form.Checkbox.superclass.initEvents.call(this);
24418         this.el.on("click", this.onClick,  this);
24419         this.el.on("change", this.onClick,  this);
24420     },
24421
24422
24423     getResizeEl : function(){
24424         return this.wrap;
24425     },
24426
24427     getPositionEl : function(){
24428         return this.wrap;
24429     },
24430
24431     // private
24432     onRender : function(ct, position){
24433         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24434         /*
24435         if(this.inputValue !== undefined){
24436             this.el.dom.value = this.inputValue;
24437         }
24438         */
24439         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24440         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24441         var viewEl = this.wrap.createChild({ 
24442             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24443         this.viewEl = viewEl;   
24444         this.wrap.on('click', this.onClick,  this); 
24445         
24446         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24447         this.el.on('propertychange', this.setFromHidden,  this);  //ie
24448         
24449         
24450         
24451         if(this.boxLabel){
24452             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24453         //    viewEl.on('click', this.onClick,  this); 
24454         }
24455         //if(this.checked){
24456             this.setChecked(this.checked);
24457         //}else{
24458             //this.checked = this.el.dom;
24459         //}
24460
24461     },
24462
24463     // private
24464     initValue : Roo.emptyFn,
24465
24466     /**
24467      * Returns the checked state of the checkbox.
24468      * @return {Boolean} True if checked, else false
24469      */
24470     getValue : function(){
24471         if(this.el){
24472             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
24473         }
24474         return this.valueOff;
24475         
24476     },
24477
24478         // private
24479     onClick : function(){ 
24480         if (this.disabled) {
24481             return;
24482         }
24483         this.setChecked(!this.checked);
24484
24485         //if(this.el.dom.checked != this.checked){
24486         //    this.setValue(this.el.dom.checked);
24487        // }
24488     },
24489
24490     /**
24491      * Sets the checked state of the checkbox.
24492      * On is always based on a string comparison between inputValue and the param.
24493      * @param {Boolean/String} value - the value to set 
24494      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
24495      */
24496     setValue : function(v,suppressEvent){
24497         
24498         
24499         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
24500         //if(this.el && this.el.dom){
24501         //    this.el.dom.checked = this.checked;
24502         //    this.el.dom.defaultChecked = this.checked;
24503         //}
24504         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
24505         //this.fireEvent("check", this, this.checked);
24506     },
24507     // private..
24508     setChecked : function(state,suppressEvent)
24509     {
24510         if (this.inSetChecked) {
24511             this.checked = state;
24512             return;
24513         }
24514         
24515     
24516         if(this.wrap){
24517             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
24518         }
24519         this.checked = state;
24520         if(suppressEvent !== true){
24521             this.fireEvent('check', this, state);
24522         }
24523         this.inSetChecked = true;
24524         this.el.dom.value = state ? this.inputValue : this.valueOff;
24525         this.inSetChecked = false;
24526         
24527     },
24528     // handle setting of hidden value by some other method!!?!?
24529     setFromHidden: function()
24530     {
24531         if(!this.el){
24532             return;
24533         }
24534         //console.log("SET FROM HIDDEN");
24535         //alert('setFrom hidden');
24536         this.setValue(this.el.dom.value);
24537     },
24538     
24539     onDestroy : function()
24540     {
24541         if(this.viewEl){
24542             Roo.get(this.viewEl).remove();
24543         }
24544          
24545         Roo.form.Checkbox.superclass.onDestroy.call(this);
24546     }
24547
24548 });/*
24549  * Based on:
24550  * Ext JS Library 1.1.1
24551  * Copyright(c) 2006-2007, Ext JS, LLC.
24552  *
24553  * Originally Released Under LGPL - original licence link has changed is not relivant.
24554  *
24555  * Fork - LGPL
24556  * <script type="text/javascript">
24557  */
24558  
24559 /**
24560  * @class Roo.form.Radio
24561  * @extends Roo.form.Checkbox
24562  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
24563  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
24564  * @constructor
24565  * Creates a new Radio
24566  * @param {Object} config Configuration options
24567  */
24568 Roo.form.Radio = function(){
24569     Roo.form.Radio.superclass.constructor.apply(this, arguments);
24570 };
24571 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
24572     inputType: 'radio',
24573
24574     /**
24575      * If this radio is part of a group, it will return the selected value
24576      * @return {String}
24577      */
24578     getGroupValue : function(){
24579         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
24580     },
24581     
24582     
24583     onRender : function(ct, position){
24584         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24585         
24586         if(this.inputValue !== undefined){
24587             this.el.dom.value = this.inputValue;
24588         }
24589          
24590         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24591         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24592         //var viewEl = this.wrap.createChild({ 
24593         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24594         //this.viewEl = viewEl;   
24595         //this.wrap.on('click', this.onClick,  this); 
24596         
24597         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24598         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
24599         
24600         
24601         
24602         if(this.boxLabel){
24603             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24604         //    viewEl.on('click', this.onClick,  this); 
24605         }
24606          if(this.checked){
24607             this.el.dom.checked =   'checked' ;
24608         }
24609          
24610     } 
24611     
24612     
24613 });//<script type="text/javascript">
24614
24615 /*
24616  * Based  Ext JS Library 1.1.1
24617  * Copyright(c) 2006-2007, Ext JS, LLC.
24618  * LGPL
24619  *
24620  */
24621  
24622 /**
24623  * @class Roo.HtmlEditorCore
24624  * @extends Roo.Component
24625  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24626  *
24627  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24628  */
24629
24630 Roo.HtmlEditorCore = function(config){
24631     
24632     
24633     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24634     
24635     
24636     this.addEvents({
24637         /**
24638          * @event initialize
24639          * Fires when the editor is fully initialized (including the iframe)
24640          * @param {Roo.HtmlEditorCore} this
24641          */
24642         initialize: true,
24643         /**
24644          * @event activate
24645          * Fires when the editor is first receives the focus. Any insertion must wait
24646          * until after this event.
24647          * @param {Roo.HtmlEditorCore} this
24648          */
24649         activate: true,
24650          /**
24651          * @event beforesync
24652          * Fires before the textarea is updated with content from the editor iframe. Return false
24653          * to cancel the sync.
24654          * @param {Roo.HtmlEditorCore} this
24655          * @param {String} html
24656          */
24657         beforesync: true,
24658          /**
24659          * @event beforepush
24660          * Fires before the iframe editor is updated with content from the textarea. Return false
24661          * to cancel the push.
24662          * @param {Roo.HtmlEditorCore} this
24663          * @param {String} html
24664          */
24665         beforepush: true,
24666          /**
24667          * @event sync
24668          * Fires when the textarea is updated with content from the editor iframe.
24669          * @param {Roo.HtmlEditorCore} this
24670          * @param {String} html
24671          */
24672         sync: true,
24673          /**
24674          * @event push
24675          * Fires when the iframe editor is updated with content from the textarea.
24676          * @param {Roo.HtmlEditorCore} this
24677          * @param {String} html
24678          */
24679         push: true,
24680         
24681         /**
24682          * @event editorevent
24683          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24684          * @param {Roo.HtmlEditorCore} this
24685          */
24686         editorevent: true
24687     });
24688     
24689     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24690     
24691     // defaults : white / black...
24692     this.applyBlacklists();
24693     
24694     
24695     
24696 };
24697
24698
24699 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24700
24701
24702      /**
24703      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24704      */
24705     
24706     owner : false,
24707     
24708      /**
24709      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24710      *                        Roo.resizable.
24711      */
24712     resizable : false,
24713      /**
24714      * @cfg {Number} height (in pixels)
24715      */   
24716     height: 300,
24717    /**
24718      * @cfg {Number} width (in pixels)
24719      */   
24720     width: 500,
24721     
24722     /**
24723      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24724      * 
24725      */
24726     stylesheets: false,
24727     
24728     // id of frame..
24729     frameId: false,
24730     
24731     // private properties
24732     validationEvent : false,
24733     deferHeight: true,
24734     initialized : false,
24735     activated : false,
24736     sourceEditMode : false,
24737     onFocus : Roo.emptyFn,
24738     iframePad:3,
24739     hideMode:'offsets',
24740     
24741     clearUp: true,
24742     
24743     // blacklist + whitelisted elements..
24744     black: false,
24745     white: false,
24746      
24747     
24748
24749     /**
24750      * Protected method that will not generally be called directly. It
24751      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24752      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24753      */
24754     getDocMarkup : function(){
24755         // body styles..
24756         var st = '';
24757         Roo.log(this.stylesheets);
24758         
24759         // inherit styels from page...?? 
24760         if (this.stylesheets === false) {
24761             
24762             Roo.get(document.head).select('style').each(function(node) {
24763                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24764             });
24765             
24766             Roo.get(document.head).select('link').each(function(node) { 
24767                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24768             });
24769             
24770         } else if (!this.stylesheets.length) {
24771                 // simple..
24772                 st = '<style type="text/css">' +
24773                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24774                    '</style>';
24775         } else {
24776             Roo.each(this.stylesheets, function(s) {
24777                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
24778             });
24779             
24780         }
24781         
24782         st +=  '<style type="text/css">' +
24783             'IMG { cursor: pointer } ' +
24784         '</style>';
24785
24786         
24787         return '<html><head>' + st  +
24788             //<style type="text/css">' +
24789             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24790             //'</style>' +
24791             ' </head><body class="roo-htmleditor-body"></body></html>';
24792     },
24793
24794     // private
24795     onRender : function(ct, position)
24796     {
24797         var _t = this;
24798         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24799         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24800         
24801         
24802         this.el.dom.style.border = '0 none';
24803         this.el.dom.setAttribute('tabIndex', -1);
24804         this.el.addClass('x-hidden hide');
24805         
24806         
24807         
24808         if(Roo.isIE){ // fix IE 1px bogus margin
24809             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24810         }
24811        
24812         
24813         this.frameId = Roo.id();
24814         
24815          
24816         
24817         var iframe = this.owner.wrap.createChild({
24818             tag: 'iframe',
24819             cls: 'form-control', // bootstrap..
24820             id: this.frameId,
24821             name: this.frameId,
24822             frameBorder : 'no',
24823             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24824         }, this.el
24825         );
24826         
24827         
24828         this.iframe = iframe.dom;
24829
24830          this.assignDocWin();
24831         
24832         this.doc.designMode = 'on';
24833        
24834         this.doc.open();
24835         this.doc.write(this.getDocMarkup());
24836         this.doc.close();
24837
24838         
24839         var task = { // must defer to wait for browser to be ready
24840             run : function(){
24841                 //console.log("run task?" + this.doc.readyState);
24842                 this.assignDocWin();
24843                 if(this.doc.body || this.doc.readyState == 'complete'){
24844                     try {
24845                         this.doc.designMode="on";
24846                     } catch (e) {
24847                         return;
24848                     }
24849                     Roo.TaskMgr.stop(task);
24850                     this.initEditor.defer(10, this);
24851                 }
24852             },
24853             interval : 10,
24854             duration: 10000,
24855             scope: this
24856         };
24857         Roo.TaskMgr.start(task);
24858
24859         
24860          
24861     },
24862
24863     // private
24864     onResize : function(w, h)
24865     {
24866          Roo.log('resize: ' +w + ',' + h );
24867         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24868         if(!this.iframe){
24869             return;
24870         }
24871         if(typeof w == 'number'){
24872             
24873             this.iframe.style.width = w + 'px';
24874         }
24875         if(typeof h == 'number'){
24876             
24877             this.iframe.style.height = h + 'px';
24878             if(this.doc){
24879                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24880             }
24881         }
24882         
24883     },
24884
24885     /**
24886      * Toggles the editor between standard and source edit mode.
24887      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24888      */
24889     toggleSourceEdit : function(sourceEditMode){
24890         
24891         this.sourceEditMode = sourceEditMode === true;
24892         
24893         if(this.sourceEditMode){
24894  
24895             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24896             
24897         }else{
24898             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24899             //this.iframe.className = '';
24900             this.deferFocus();
24901         }
24902         //this.setSize(this.owner.wrap.getSize());
24903         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24904     },
24905
24906     
24907   
24908
24909     /**
24910      * Protected method that will not generally be called directly. If you need/want
24911      * custom HTML cleanup, this is the method you should override.
24912      * @param {String} html The HTML to be cleaned
24913      * return {String} The cleaned HTML
24914      */
24915     cleanHtml : function(html){
24916         html = String(html);
24917         if(html.length > 5){
24918             if(Roo.isSafari){ // strip safari nonsense
24919                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24920             }
24921         }
24922         if(html == '&nbsp;'){
24923             html = '';
24924         }
24925         return html;
24926     },
24927
24928     /**
24929      * HTML Editor -> Textarea
24930      * Protected method that will not generally be called directly. Syncs the contents
24931      * of the editor iframe with the textarea.
24932      */
24933     syncValue : function(){
24934         if(this.initialized){
24935             var bd = (this.doc.body || this.doc.documentElement);
24936             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24937             var html = bd.innerHTML;
24938             if(Roo.isSafari){
24939                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24940                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24941                 if(m && m[1]){
24942                     html = '<div style="'+m[0]+'">' + html + '</div>';
24943                 }
24944             }
24945             html = this.cleanHtml(html);
24946             // fix up the special chars.. normaly like back quotes in word...
24947             // however we do not want to do this with chinese..
24948             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
24949                 var cc = b.charCodeAt();
24950                 if (
24951                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24952                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24953                     (cc >= 0xf900 && cc < 0xfb00 )
24954                 ) {
24955                         return b;
24956                 }
24957                 return "&#"+cc+";" 
24958             });
24959             if(this.owner.fireEvent('beforesync', this, html) !== false){
24960                 this.el.dom.value = html;
24961                 this.owner.fireEvent('sync', this, html);
24962             }
24963         }
24964     },
24965
24966     /**
24967      * Protected method that will not generally be called directly. Pushes the value of the textarea
24968      * into the iframe editor.
24969      */
24970     pushValue : function(){
24971         if(this.initialized){
24972             var v = this.el.dom.value.trim();
24973             
24974 //            if(v.length < 1){
24975 //                v = '&#160;';
24976 //            }
24977             
24978             if(this.owner.fireEvent('beforepush', this, v) !== false){
24979                 var d = (this.doc.body || this.doc.documentElement);
24980                 d.innerHTML = v;
24981                 this.cleanUpPaste();
24982                 this.el.dom.value = d.innerHTML;
24983                 this.owner.fireEvent('push', this, v);
24984             }
24985         }
24986     },
24987
24988     // private
24989     deferFocus : function(){
24990         this.focus.defer(10, this);
24991     },
24992
24993     // doc'ed in Field
24994     focus : function(){
24995         if(this.win && !this.sourceEditMode){
24996             this.win.focus();
24997         }else{
24998             this.el.focus();
24999         }
25000     },
25001     
25002     assignDocWin: function()
25003     {
25004         var iframe = this.iframe;
25005         
25006          if(Roo.isIE){
25007             this.doc = iframe.contentWindow.document;
25008             this.win = iframe.contentWindow;
25009         } else {
25010 //            if (!Roo.get(this.frameId)) {
25011 //                return;
25012 //            }
25013 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25014 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25015             
25016             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25017                 return;
25018             }
25019             
25020             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25021             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25022         }
25023     },
25024     
25025     // private
25026     initEditor : function(){
25027         //console.log("INIT EDITOR");
25028         this.assignDocWin();
25029         
25030         
25031         
25032         this.doc.designMode="on";
25033         this.doc.open();
25034         this.doc.write(this.getDocMarkup());
25035         this.doc.close();
25036         
25037         var dbody = (this.doc.body || this.doc.documentElement);
25038         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25039         // this copies styles from the containing element into thsi one..
25040         // not sure why we need all of this..
25041         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25042         
25043         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25044         //ss['background-attachment'] = 'fixed'; // w3c
25045         dbody.bgProperties = 'fixed'; // ie
25046         //Roo.DomHelper.applyStyles(dbody, ss);
25047         Roo.EventManager.on(this.doc, {
25048             //'mousedown': this.onEditorEvent,
25049             'mouseup': this.onEditorEvent,
25050             'dblclick': this.onEditorEvent,
25051             'click': this.onEditorEvent,
25052             'keyup': this.onEditorEvent,
25053             buffer:100,
25054             scope: this
25055         });
25056         if(Roo.isGecko){
25057             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25058         }
25059         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25060             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25061         }
25062         this.initialized = true;
25063
25064         this.owner.fireEvent('initialize', this);
25065         this.pushValue();
25066     },
25067
25068     // private
25069     onDestroy : function(){
25070         
25071         
25072         
25073         if(this.rendered){
25074             
25075             //for (var i =0; i < this.toolbars.length;i++) {
25076             //    // fixme - ask toolbars for heights?
25077             //    this.toolbars[i].onDestroy();
25078            // }
25079             
25080             //this.wrap.dom.innerHTML = '';
25081             //this.wrap.remove();
25082         }
25083     },
25084
25085     // private
25086     onFirstFocus : function(){
25087         
25088         this.assignDocWin();
25089         
25090         
25091         this.activated = true;
25092          
25093     
25094         if(Roo.isGecko){ // prevent silly gecko errors
25095             this.win.focus();
25096             var s = this.win.getSelection();
25097             if(!s.focusNode || s.focusNode.nodeType != 3){
25098                 var r = s.getRangeAt(0);
25099                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25100                 r.collapse(true);
25101                 this.deferFocus();
25102             }
25103             try{
25104                 this.execCmd('useCSS', true);
25105                 this.execCmd('styleWithCSS', false);
25106             }catch(e){}
25107         }
25108         this.owner.fireEvent('activate', this);
25109     },
25110
25111     // private
25112     adjustFont: function(btn){
25113         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25114         //if(Roo.isSafari){ // safari
25115         //    adjust *= 2;
25116        // }
25117         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25118         if(Roo.isSafari){ // safari
25119             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25120             v =  (v < 10) ? 10 : v;
25121             v =  (v > 48) ? 48 : v;
25122             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25123             
25124         }
25125         
25126         
25127         v = Math.max(1, v+adjust);
25128         
25129         this.execCmd('FontSize', v  );
25130     },
25131
25132     onEditorEvent : function(e){
25133         this.owner.fireEvent('editorevent', this, e);
25134       //  this.updateToolbar();
25135         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25136     },
25137
25138     insertTag : function(tg)
25139     {
25140         // could be a bit smarter... -> wrap the current selected tRoo..
25141         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
25142             
25143             range = this.createRange(this.getSelection());
25144             var wrappingNode = this.doc.createElement(tg.toLowerCase());
25145             wrappingNode.appendChild(range.extractContents());
25146             range.insertNode(wrappingNode);
25147
25148             return;
25149             
25150             
25151             
25152         }
25153         this.execCmd("formatblock",   tg);
25154         
25155     },
25156     
25157     insertText : function(txt)
25158     {
25159         
25160         
25161         var range = this.createRange();
25162         range.deleteContents();
25163                //alert(Sender.getAttribute('label'));
25164                
25165         range.insertNode(this.doc.createTextNode(txt));
25166     } ,
25167     
25168      
25169
25170     /**
25171      * Executes a Midas editor command on the editor document and performs necessary focus and
25172      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25173      * @param {String} cmd The Midas command
25174      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25175      */
25176     relayCmd : function(cmd, value){
25177         this.win.focus();
25178         this.execCmd(cmd, value);
25179         this.owner.fireEvent('editorevent', this);
25180         //this.updateToolbar();
25181         this.owner.deferFocus();
25182     },
25183
25184     /**
25185      * Executes a Midas editor command directly on the editor document.
25186      * For visual commands, you should use {@link #relayCmd} instead.
25187      * <b>This should only be called after the editor is initialized.</b>
25188      * @param {String} cmd The Midas command
25189      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25190      */
25191     execCmd : function(cmd, value){
25192         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25193         this.syncValue();
25194     },
25195  
25196  
25197    
25198     /**
25199      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25200      * to insert tRoo.
25201      * @param {String} text | dom node.. 
25202      */
25203     insertAtCursor : function(text)
25204     {
25205         
25206         
25207         
25208         if(!this.activated){
25209             return;
25210         }
25211         /*
25212         if(Roo.isIE){
25213             this.win.focus();
25214             var r = this.doc.selection.createRange();
25215             if(r){
25216                 r.collapse(true);
25217                 r.pasteHTML(text);
25218                 this.syncValue();
25219                 this.deferFocus();
25220             
25221             }
25222             return;
25223         }
25224         */
25225         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25226             this.win.focus();
25227             
25228             
25229             // from jquery ui (MIT licenced)
25230             var range, node;
25231             var win = this.win;
25232             
25233             if (win.getSelection && win.getSelection().getRangeAt) {
25234                 range = win.getSelection().getRangeAt(0);
25235                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25236                 range.insertNode(node);
25237             } else if (win.document.selection && win.document.selection.createRange) {
25238                 // no firefox support
25239                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25240                 win.document.selection.createRange().pasteHTML(txt);
25241             } else {
25242                 // no firefox support
25243                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25244                 this.execCmd('InsertHTML', txt);
25245             } 
25246             
25247             this.syncValue();
25248             
25249             this.deferFocus();
25250         }
25251     },
25252  // private
25253     mozKeyPress : function(e){
25254         if(e.ctrlKey){
25255             var c = e.getCharCode(), cmd;
25256           
25257             if(c > 0){
25258                 c = String.fromCharCode(c).toLowerCase();
25259                 switch(c){
25260                     case 'b':
25261                         cmd = 'bold';
25262                         break;
25263                     case 'i':
25264                         cmd = 'italic';
25265                         break;
25266                     
25267                     case 'u':
25268                         cmd = 'underline';
25269                         break;
25270                     
25271                     case 'v':
25272                         this.cleanUpPaste.defer(100, this);
25273                         return;
25274                         
25275                 }
25276                 if(cmd){
25277                     this.win.focus();
25278                     this.execCmd(cmd);
25279                     this.deferFocus();
25280                     e.preventDefault();
25281                 }
25282                 
25283             }
25284         }
25285     },
25286
25287     // private
25288     fixKeys : function(){ // load time branching for fastest keydown performance
25289         if(Roo.isIE){
25290             return function(e){
25291                 var k = e.getKey(), r;
25292                 if(k == e.TAB){
25293                     e.stopEvent();
25294                     r = this.doc.selection.createRange();
25295                     if(r){
25296                         r.collapse(true);
25297                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25298                         this.deferFocus();
25299                     }
25300                     return;
25301                 }
25302                 
25303                 if(k == e.ENTER){
25304                     r = this.doc.selection.createRange();
25305                     if(r){
25306                         var target = r.parentElement();
25307                         if(!target || target.tagName.toLowerCase() != 'li'){
25308                             e.stopEvent();
25309                             r.pasteHTML('<br />');
25310                             r.collapse(false);
25311                             r.select();
25312                         }
25313                     }
25314                 }
25315                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25316                     this.cleanUpPaste.defer(100, this);
25317                     return;
25318                 }
25319                 
25320                 
25321             };
25322         }else if(Roo.isOpera){
25323             return function(e){
25324                 var k = e.getKey();
25325                 if(k == e.TAB){
25326                     e.stopEvent();
25327                     this.win.focus();
25328                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25329                     this.deferFocus();
25330                 }
25331                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25332                     this.cleanUpPaste.defer(100, this);
25333                     return;
25334                 }
25335                 
25336             };
25337         }else if(Roo.isSafari){
25338             return function(e){
25339                 var k = e.getKey();
25340                 
25341                 if(k == e.TAB){
25342                     e.stopEvent();
25343                     this.execCmd('InsertText','\t');
25344                     this.deferFocus();
25345                     return;
25346                 }
25347                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25348                     this.cleanUpPaste.defer(100, this);
25349                     return;
25350                 }
25351                 
25352              };
25353         }
25354     }(),
25355     
25356     getAllAncestors: function()
25357     {
25358         var p = this.getSelectedNode();
25359         var a = [];
25360         if (!p) {
25361             a.push(p); // push blank onto stack..
25362             p = this.getParentElement();
25363         }
25364         
25365         
25366         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25367             a.push(p);
25368             p = p.parentNode;
25369         }
25370         a.push(this.doc.body);
25371         return a;
25372     },
25373     lastSel : false,
25374     lastSelNode : false,
25375     
25376     
25377     getSelection : function() 
25378     {
25379         this.assignDocWin();
25380         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25381     },
25382     
25383     getSelectedNode: function() 
25384     {
25385         // this may only work on Gecko!!!
25386         
25387         // should we cache this!!!!
25388         
25389         
25390         
25391          
25392         var range = this.createRange(this.getSelection()).cloneRange();
25393         
25394         if (Roo.isIE) {
25395             var parent = range.parentElement();
25396             while (true) {
25397                 var testRange = range.duplicate();
25398                 testRange.moveToElementText(parent);
25399                 if (testRange.inRange(range)) {
25400                     break;
25401                 }
25402                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25403                     break;
25404                 }
25405                 parent = parent.parentElement;
25406             }
25407             return parent;
25408         }
25409         
25410         // is ancestor a text element.
25411         var ac =  range.commonAncestorContainer;
25412         if (ac.nodeType == 3) {
25413             ac = ac.parentNode;
25414         }
25415         
25416         var ar = ac.childNodes;
25417          
25418         var nodes = [];
25419         var other_nodes = [];
25420         var has_other_nodes = false;
25421         for (var i=0;i<ar.length;i++) {
25422             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25423                 continue;
25424             }
25425             // fullly contained node.
25426             
25427             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25428                 nodes.push(ar[i]);
25429                 continue;
25430             }
25431             
25432             // probably selected..
25433             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25434                 other_nodes.push(ar[i]);
25435                 continue;
25436             }
25437             // outer..
25438             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25439                 continue;
25440             }
25441             
25442             
25443             has_other_nodes = true;
25444         }
25445         if (!nodes.length && other_nodes.length) {
25446             nodes= other_nodes;
25447         }
25448         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25449             return false;
25450         }
25451         
25452         return nodes[0];
25453     },
25454     createRange: function(sel)
25455     {
25456         // this has strange effects when using with 
25457         // top toolbar - not sure if it's a great idea.
25458         //this.editor.contentWindow.focus();
25459         if (typeof sel != "undefined") {
25460             try {
25461                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25462             } catch(e) {
25463                 return this.doc.createRange();
25464             }
25465         } else {
25466             return this.doc.createRange();
25467         }
25468     },
25469     getParentElement: function()
25470     {
25471         
25472         this.assignDocWin();
25473         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25474         
25475         var range = this.createRange(sel);
25476          
25477         try {
25478             var p = range.commonAncestorContainer;
25479             while (p.nodeType == 3) { // text node
25480                 p = p.parentNode;
25481             }
25482             return p;
25483         } catch (e) {
25484             return null;
25485         }
25486     
25487     },
25488     /***
25489      *
25490      * Range intersection.. the hard stuff...
25491      *  '-1' = before
25492      *  '0' = hits..
25493      *  '1' = after.
25494      *         [ -- selected range --- ]
25495      *   [fail]                        [fail]
25496      *
25497      *    basically..
25498      *      if end is before start or  hits it. fail.
25499      *      if start is after end or hits it fail.
25500      *
25501      *   if either hits (but other is outside. - then it's not 
25502      *   
25503      *    
25504      **/
25505     
25506     
25507     // @see http://www.thismuchiknow.co.uk/?p=64.
25508     rangeIntersectsNode : function(range, node)
25509     {
25510         var nodeRange = node.ownerDocument.createRange();
25511         try {
25512             nodeRange.selectNode(node);
25513         } catch (e) {
25514             nodeRange.selectNodeContents(node);
25515         }
25516     
25517         var rangeStartRange = range.cloneRange();
25518         rangeStartRange.collapse(true);
25519     
25520         var rangeEndRange = range.cloneRange();
25521         rangeEndRange.collapse(false);
25522     
25523         var nodeStartRange = nodeRange.cloneRange();
25524         nodeStartRange.collapse(true);
25525     
25526         var nodeEndRange = nodeRange.cloneRange();
25527         nodeEndRange.collapse(false);
25528     
25529         return rangeStartRange.compareBoundaryPoints(
25530                  Range.START_TO_START, nodeEndRange) == -1 &&
25531                rangeEndRange.compareBoundaryPoints(
25532                  Range.START_TO_START, nodeStartRange) == 1;
25533         
25534          
25535     },
25536     rangeCompareNode : function(range, node)
25537     {
25538         var nodeRange = node.ownerDocument.createRange();
25539         try {
25540             nodeRange.selectNode(node);
25541         } catch (e) {
25542             nodeRange.selectNodeContents(node);
25543         }
25544         
25545         
25546         range.collapse(true);
25547     
25548         nodeRange.collapse(true);
25549      
25550         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25551         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25552          
25553         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25554         
25555         var nodeIsBefore   =  ss == 1;
25556         var nodeIsAfter    = ee == -1;
25557         
25558         if (nodeIsBefore && nodeIsAfter)
25559             return 0; // outer
25560         if (!nodeIsBefore && nodeIsAfter)
25561             return 1; //right trailed.
25562         
25563         if (nodeIsBefore && !nodeIsAfter)
25564             return 2;  // left trailed.
25565         // fully contined.
25566         return 3;
25567     },
25568
25569     // private? - in a new class?
25570     cleanUpPaste :  function()
25571     {
25572         // cleans up the whole document..
25573         Roo.log('cleanuppaste');
25574         
25575         this.cleanUpChildren(this.doc.body);
25576         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25577         if (clean != this.doc.body.innerHTML) {
25578             this.doc.body.innerHTML = clean;
25579         }
25580         
25581     },
25582     
25583     cleanWordChars : function(input) {// change the chars to hex code
25584         var he = Roo.HtmlEditorCore;
25585         
25586         var output = input;
25587         Roo.each(he.swapCodes, function(sw) { 
25588             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25589             
25590             output = output.replace(swapper, sw[1]);
25591         });
25592         
25593         return output;
25594     },
25595     
25596     
25597     cleanUpChildren : function (n)
25598     {
25599         if (!n.childNodes.length) {
25600             return;
25601         }
25602         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25603            this.cleanUpChild(n.childNodes[i]);
25604         }
25605     },
25606     
25607     
25608         
25609     
25610     cleanUpChild : function (node)
25611     {
25612         var ed = this;
25613         //console.log(node);
25614         if (node.nodeName == "#text") {
25615             // clean up silly Windows -- stuff?
25616             return; 
25617         }
25618         if (node.nodeName == "#comment") {
25619             node.parentNode.removeChild(node);
25620             // clean up silly Windows -- stuff?
25621             return; 
25622         }
25623         var lcname = node.tagName.toLowerCase();
25624         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25625         // whitelist of tags..
25626         
25627         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25628             // remove node.
25629             node.parentNode.removeChild(node);
25630             return;
25631             
25632         }
25633         
25634         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25635         
25636         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25637         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25638         
25639         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25640         //    remove_keep_children = true;
25641         //}
25642         
25643         if (remove_keep_children) {
25644             this.cleanUpChildren(node);
25645             // inserts everything just before this node...
25646             while (node.childNodes.length) {
25647                 var cn = node.childNodes[0];
25648                 node.removeChild(cn);
25649                 node.parentNode.insertBefore(cn, node);
25650             }
25651             node.parentNode.removeChild(node);
25652             return;
25653         }
25654         
25655         if (!node.attributes || !node.attributes.length) {
25656             this.cleanUpChildren(node);
25657             return;
25658         }
25659         
25660         function cleanAttr(n,v)
25661         {
25662             
25663             if (v.match(/^\./) || v.match(/^\//)) {
25664                 return;
25665             }
25666             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
25667                 return;
25668             }
25669             if (v.match(/^#/)) {
25670                 return;
25671             }
25672 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25673             node.removeAttribute(n);
25674             
25675         }
25676         
25677         var cwhite = this.cwhite;
25678         var cblack = this.cblack;
25679             
25680         function cleanStyle(n,v)
25681         {
25682             if (v.match(/expression/)) { //XSS?? should we even bother..
25683                 node.removeAttribute(n);
25684                 return;
25685             }
25686             
25687             var parts = v.split(/;/);
25688             var clean = [];
25689             
25690             Roo.each(parts, function(p) {
25691                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25692                 if (!p.length) {
25693                     return true;
25694                 }
25695                 var l = p.split(':').shift().replace(/\s+/g,'');
25696                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25697                 
25698                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25699 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25700                     //node.removeAttribute(n);
25701                     return true;
25702                 }
25703                 //Roo.log()
25704                 // only allow 'c whitelisted system attributes'
25705                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25706 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25707                     //node.removeAttribute(n);
25708                     return true;
25709                 }
25710                 
25711                 
25712                  
25713                 
25714                 clean.push(p);
25715                 return true;
25716             });
25717             if (clean.length) { 
25718                 node.setAttribute(n, clean.join(';'));
25719             } else {
25720                 node.removeAttribute(n);
25721             }
25722             
25723         }
25724         
25725         
25726         for (var i = node.attributes.length-1; i > -1 ; i--) {
25727             var a = node.attributes[i];
25728             //console.log(a);
25729             
25730             if (a.name.toLowerCase().substr(0,2)=='on')  {
25731                 node.removeAttribute(a.name);
25732                 continue;
25733             }
25734             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25735                 node.removeAttribute(a.name);
25736                 continue;
25737             }
25738             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25739                 cleanAttr(a.name,a.value); // fixme..
25740                 continue;
25741             }
25742             if (a.name == 'style') {
25743                 cleanStyle(a.name,a.value);
25744                 continue;
25745             }
25746             /// clean up MS crap..
25747             // tecnically this should be a list of valid class'es..
25748             
25749             
25750             if (a.name == 'class') {
25751                 if (a.value.match(/^Mso/)) {
25752                     node.className = '';
25753                 }
25754                 
25755                 if (a.value.match(/body/)) {
25756                     node.className = '';
25757                 }
25758                 continue;
25759             }
25760             
25761             // style cleanup!?
25762             // class cleanup?
25763             
25764         }
25765         
25766         
25767         this.cleanUpChildren(node);
25768         
25769         
25770     },
25771     /**
25772      * Clean up MS wordisms...
25773      */
25774     cleanWord : function(node)
25775     {
25776         var _t = this;
25777         var cleanWordChildren = function()
25778         {
25779             if (!node.childNodes.length) {
25780                 return;
25781             }
25782             for (var i = node.childNodes.length-1; i > -1 ; i--) {
25783                _t.cleanWord(node.childNodes[i]);
25784             }
25785         }
25786         
25787         
25788         if (!node) {
25789             this.cleanWord(this.doc.body);
25790             return;
25791         }
25792         if (node.nodeName == "#text") {
25793             // clean up silly Windows -- stuff?
25794             return; 
25795         }
25796         if (node.nodeName == "#comment") {
25797             node.parentNode.removeChild(node);
25798             // clean up silly Windows -- stuff?
25799             return; 
25800         }
25801         
25802         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25803             node.parentNode.removeChild(node);
25804             return;
25805         }
25806         
25807         // remove - but keep children..
25808         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
25809             while (node.childNodes.length) {
25810                 var cn = node.childNodes[0];
25811                 node.removeChild(cn);
25812                 node.parentNode.insertBefore(cn, node);
25813             }
25814             node.parentNode.removeChild(node);
25815             cleanWordChildren();
25816             return;
25817         }
25818         // clean styles
25819         if (node.className.length) {
25820             
25821             var cn = node.className.split(/\W+/);
25822             var cna = [];
25823             Roo.each(cn, function(cls) {
25824                 if (cls.match(/Mso[a-zA-Z]+/)) {
25825                     return;
25826                 }
25827                 cna.push(cls);
25828             });
25829             node.className = cna.length ? cna.join(' ') : '';
25830             if (!cna.length) {
25831                 node.removeAttribute("class");
25832             }
25833         }
25834         
25835         if (node.hasAttribute("lang")) {
25836             node.removeAttribute("lang");
25837         }
25838         
25839         if (node.hasAttribute("style")) {
25840             
25841             var styles = node.getAttribute("style").split(";");
25842             var nstyle = [];
25843             Roo.each(styles, function(s) {
25844                 if (!s.match(/:/)) {
25845                     return;
25846                 }
25847                 var kv = s.split(":");
25848                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25849                     return;
25850                 }
25851                 // what ever is left... we allow.
25852                 nstyle.push(s);
25853             });
25854             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25855             if (!nstyle.length) {
25856                 node.removeAttribute('style');
25857             }
25858         }
25859         
25860         cleanWordChildren();
25861         
25862         
25863     },
25864     domToHTML : function(currentElement, depth, nopadtext) {
25865         
25866         depth = depth || 0;
25867         nopadtext = nopadtext || false;
25868     
25869         if (!currentElement) {
25870             return this.domToHTML(this.doc.body);
25871         }
25872         
25873         //Roo.log(currentElement);
25874         var j;
25875         var allText = false;
25876         var nodeName = currentElement.nodeName;
25877         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25878         
25879         if  (nodeName == '#text') {
25880             return currentElement.nodeValue;
25881         }
25882         
25883         
25884         var ret = '';
25885         if (nodeName != 'BODY') {
25886              
25887             var i = 0;
25888             // Prints the node tagName, such as <A>, <IMG>, etc
25889             if (tagName) {
25890                 var attr = [];
25891                 for(i = 0; i < currentElement.attributes.length;i++) {
25892                     // quoting?
25893                     var aname = currentElement.attributes.item(i).name;
25894                     if (!currentElement.attributes.item(i).value.length) {
25895                         continue;
25896                     }
25897                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25898                 }
25899                 
25900                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25901             } 
25902             else {
25903                 
25904                 // eack
25905             }
25906         } else {
25907             tagName = false;
25908         }
25909         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25910             return ret;
25911         }
25912         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25913             nopadtext = true;
25914         }
25915         
25916         
25917         // Traverse the tree
25918         i = 0;
25919         var currentElementChild = currentElement.childNodes.item(i);
25920         var allText = true;
25921         var innerHTML  = '';
25922         lastnode = '';
25923         while (currentElementChild) {
25924             // Formatting code (indent the tree so it looks nice on the screen)
25925             var nopad = nopadtext;
25926             if (lastnode == 'SPAN') {
25927                 nopad  = true;
25928             }
25929             // text
25930             if  (currentElementChild.nodeName == '#text') {
25931                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25932                 if (!nopad && toadd.length > 80) {
25933                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25934                 }
25935                 innerHTML  += toadd;
25936                 
25937                 i++;
25938                 currentElementChild = currentElement.childNodes.item(i);
25939                 lastNode = '';
25940                 continue;
25941             }
25942             allText = false;
25943             
25944             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25945                 
25946             // Recursively traverse the tree structure of the child node
25947             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25948             lastnode = currentElementChild.nodeName;
25949             i++;
25950             currentElementChild=currentElement.childNodes.item(i);
25951         }
25952         
25953         ret += innerHTML;
25954         
25955         if (!allText) {
25956                 // The remaining code is mostly for formatting the tree
25957             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25958         }
25959         
25960         
25961         if (tagName) {
25962             ret+= "</"+tagName+">";
25963         }
25964         return ret;
25965         
25966     },
25967         
25968     applyBlacklists : function()
25969     {
25970         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25971         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25972         
25973         this.white = [];
25974         this.black = [];
25975         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25976             if (b.indexOf(tag) > -1) {
25977                 return;
25978             }
25979             this.white.push(tag);
25980             
25981         }, this);
25982         
25983         Roo.each(w, function(tag) {
25984             if (b.indexOf(tag) > -1) {
25985                 return;
25986             }
25987             if (this.white.indexOf(tag) > -1) {
25988                 return;
25989             }
25990             this.white.push(tag);
25991             
25992         }, this);
25993         
25994         
25995         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25996             if (w.indexOf(tag) > -1) {
25997                 return;
25998             }
25999             this.black.push(tag);
26000             
26001         }, this);
26002         
26003         Roo.each(b, function(tag) {
26004             if (w.indexOf(tag) > -1) {
26005                 return;
26006             }
26007             if (this.black.indexOf(tag) > -1) {
26008                 return;
26009             }
26010             this.black.push(tag);
26011             
26012         }, this);
26013         
26014         
26015         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
26016         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
26017         
26018         this.cwhite = [];
26019         this.cblack = [];
26020         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26021             if (b.indexOf(tag) > -1) {
26022                 return;
26023             }
26024             this.cwhite.push(tag);
26025             
26026         }, this);
26027         
26028         Roo.each(w, function(tag) {
26029             if (b.indexOf(tag) > -1) {
26030                 return;
26031             }
26032             if (this.cwhite.indexOf(tag) > -1) {
26033                 return;
26034             }
26035             this.cwhite.push(tag);
26036             
26037         }, this);
26038         
26039         
26040         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26041             if (w.indexOf(tag) > -1) {
26042                 return;
26043             }
26044             this.cblack.push(tag);
26045             
26046         }, this);
26047         
26048         Roo.each(b, function(tag) {
26049             if (w.indexOf(tag) > -1) {
26050                 return;
26051             }
26052             if (this.cblack.indexOf(tag) > -1) {
26053                 return;
26054             }
26055             this.cblack.push(tag);
26056             
26057         }, this);
26058     }
26059     
26060     // hide stuff that is not compatible
26061     /**
26062      * @event blur
26063      * @hide
26064      */
26065     /**
26066      * @event change
26067      * @hide
26068      */
26069     /**
26070      * @event focus
26071      * @hide
26072      */
26073     /**
26074      * @event specialkey
26075      * @hide
26076      */
26077     /**
26078      * @cfg {String} fieldClass @hide
26079      */
26080     /**
26081      * @cfg {String} focusClass @hide
26082      */
26083     /**
26084      * @cfg {String} autoCreate @hide
26085      */
26086     /**
26087      * @cfg {String} inputType @hide
26088      */
26089     /**
26090      * @cfg {String} invalidClass @hide
26091      */
26092     /**
26093      * @cfg {String} invalidText @hide
26094      */
26095     /**
26096      * @cfg {String} msgFx @hide
26097      */
26098     /**
26099      * @cfg {String} validateOnBlur @hide
26100      */
26101 });
26102
26103 Roo.HtmlEditorCore.white = [
26104         'area', 'br', 'img', 'input', 'hr', 'wbr',
26105         
26106        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26107        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26108        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26109        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26110        'table',   'ul',         'xmp', 
26111        
26112        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26113       'thead',   'tr', 
26114      
26115       'dir', 'menu', 'ol', 'ul', 'dl',
26116        
26117       'embed',  'object'
26118 ];
26119
26120
26121 Roo.HtmlEditorCore.black = [
26122     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26123         'applet', // 
26124         'base',   'basefont', 'bgsound', 'blink',  'body', 
26125         'frame',  'frameset', 'head',    'html',   'ilayer', 
26126         'iframe', 'layer',  'link',     'meta',    'object',   
26127         'script', 'style' ,'title',  'xml' // clean later..
26128 ];
26129 Roo.HtmlEditorCore.clean = [
26130     'script', 'style', 'title', 'xml'
26131 ];
26132 Roo.HtmlEditorCore.remove = [
26133     'font'
26134 ];
26135 // attributes..
26136
26137 Roo.HtmlEditorCore.ablack = [
26138     'on'
26139 ];
26140     
26141 Roo.HtmlEditorCore.aclean = [ 
26142     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26143 ];
26144
26145 // protocols..
26146 Roo.HtmlEditorCore.pwhite= [
26147         'http',  'https',  'mailto'
26148 ];
26149
26150 // white listed style attributes.
26151 Roo.HtmlEditorCore.cwhite= [
26152       //  'text-align', /// default is to allow most things..
26153       
26154          
26155 //        'font-size'//??
26156 ];
26157
26158 // black listed style attributes.
26159 Roo.HtmlEditorCore.cblack= [
26160       //  'font-size' -- this can be set by the project 
26161 ];
26162
26163
26164 Roo.HtmlEditorCore.swapCodes   =[ 
26165     [    8211, "--" ], 
26166     [    8212, "--" ], 
26167     [    8216,  "'" ],  
26168     [    8217, "'" ],  
26169     [    8220, '"' ],  
26170     [    8221, '"' ],  
26171     [    8226, "*" ],  
26172     [    8230, "..." ]
26173 ]; 
26174
26175     //<script type="text/javascript">
26176
26177 /*
26178  * Ext JS Library 1.1.1
26179  * Copyright(c) 2006-2007, Ext JS, LLC.
26180  * Licence LGPL
26181  * 
26182  */
26183  
26184  
26185 Roo.form.HtmlEditor = function(config){
26186     
26187     
26188     
26189     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
26190     
26191     if (!this.toolbars) {
26192         this.toolbars = [];
26193     }
26194     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26195     
26196     
26197 };
26198
26199 /**
26200  * @class Roo.form.HtmlEditor
26201  * @extends Roo.form.Field
26202  * Provides a lightweight HTML Editor component.
26203  *
26204  * This has been tested on Fireforx / Chrome.. IE may not be so great..
26205  * 
26206  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
26207  * supported by this editor.</b><br/><br/>
26208  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
26209  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
26210  */
26211 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
26212     /**
26213      * @cfg {Boolean} clearUp
26214      */
26215     clearUp : true,
26216       /**
26217      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26218      */
26219     toolbars : false,
26220    
26221      /**
26222      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26223      *                        Roo.resizable.
26224      */
26225     resizable : false,
26226      /**
26227      * @cfg {Number} height (in pixels)
26228      */   
26229     height: 300,
26230    /**
26231      * @cfg {Number} width (in pixels)
26232      */   
26233     width: 500,
26234     
26235     /**
26236      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26237      * 
26238      */
26239     stylesheets: false,
26240     
26241     
26242      /**
26243      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
26244      * 
26245      */
26246     cblack: false,
26247     /**
26248      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
26249      * 
26250      */
26251     cwhite: false,
26252     
26253      /**
26254      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
26255      * 
26256      */
26257     black: false,
26258     /**
26259      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
26260      * 
26261      */
26262     white: false,
26263     
26264     // id of frame..
26265     frameId: false,
26266     
26267     // private properties
26268     validationEvent : false,
26269     deferHeight: true,
26270     initialized : false,
26271     activated : false,
26272     
26273     onFocus : Roo.emptyFn,
26274     iframePad:3,
26275     hideMode:'offsets',
26276     
26277     actionMode : 'container', // defaults to hiding it...
26278     
26279     defaultAutoCreate : { // modified by initCompnoent..
26280         tag: "textarea",
26281         style:"width:500px;height:300px;",
26282         autocomplete: "off"
26283     },
26284
26285     // private
26286     initComponent : function(){
26287         this.addEvents({
26288             /**
26289              * @event initialize
26290              * Fires when the editor is fully initialized (including the iframe)
26291              * @param {HtmlEditor} this
26292              */
26293             initialize: true,
26294             /**
26295              * @event activate
26296              * Fires when the editor is first receives the focus. Any insertion must wait
26297              * until after this event.
26298              * @param {HtmlEditor} this
26299              */
26300             activate: true,
26301              /**
26302              * @event beforesync
26303              * Fires before the textarea is updated with content from the editor iframe. Return false
26304              * to cancel the sync.
26305              * @param {HtmlEditor} this
26306              * @param {String} html
26307              */
26308             beforesync: true,
26309              /**
26310              * @event beforepush
26311              * Fires before the iframe editor is updated with content from the textarea. Return false
26312              * to cancel the push.
26313              * @param {HtmlEditor} this
26314              * @param {String} html
26315              */
26316             beforepush: true,
26317              /**
26318              * @event sync
26319              * Fires when the textarea is updated with content from the editor iframe.
26320              * @param {HtmlEditor} this
26321              * @param {String} html
26322              */
26323             sync: true,
26324              /**
26325              * @event push
26326              * Fires when the iframe editor is updated with content from the textarea.
26327              * @param {HtmlEditor} this
26328              * @param {String} html
26329              */
26330             push: true,
26331              /**
26332              * @event editmodechange
26333              * Fires when the editor switches edit modes
26334              * @param {HtmlEditor} this
26335              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26336              */
26337             editmodechange: true,
26338             /**
26339              * @event editorevent
26340              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26341              * @param {HtmlEditor} this
26342              */
26343             editorevent: true,
26344             /**
26345              * @event firstfocus
26346              * Fires when on first focus - needed by toolbars..
26347              * @param {HtmlEditor} this
26348              */
26349             firstfocus: true,
26350             /**
26351              * @event autosave
26352              * Auto save the htmlEditor value as a file into Events
26353              * @param {HtmlEditor} this
26354              */
26355             autosave: true,
26356             /**
26357              * @event savedpreview
26358              * preview the saved version of htmlEditor
26359              * @param {HtmlEditor} this
26360              */
26361             savedpreview: true
26362         });
26363         this.defaultAutoCreate =  {
26364             tag: "textarea",
26365             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
26366             autocomplete: "off"
26367         };
26368     },
26369
26370     /**
26371      * Protected method that will not generally be called directly. It
26372      * is called when the editor creates its toolbar. Override this method if you need to
26373      * add custom toolbar buttons.
26374      * @param {HtmlEditor} editor
26375      */
26376     createToolbar : function(editor){
26377         Roo.log("create toolbars");
26378         if (!editor.toolbars || !editor.toolbars.length) {
26379             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
26380         }
26381         
26382         for (var i =0 ; i < editor.toolbars.length;i++) {
26383             editor.toolbars[i] = Roo.factory(
26384                     typeof(editor.toolbars[i]) == 'string' ?
26385                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
26386                 Roo.form.HtmlEditor);
26387             editor.toolbars[i].init(editor);
26388         }
26389          
26390         
26391     },
26392
26393      
26394     // private
26395     onRender : function(ct, position)
26396     {
26397         var _t = this;
26398         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
26399         
26400         this.wrap = this.el.wrap({
26401             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26402         });
26403         
26404         this.editorcore.onRender(ct, position);
26405          
26406         if (this.resizable) {
26407             this.resizeEl = new Roo.Resizable(this.wrap, {
26408                 pinned : true,
26409                 wrap: true,
26410                 dynamic : true,
26411                 minHeight : this.height,
26412                 height: this.height,
26413                 handles : this.resizable,
26414                 width: this.width,
26415                 listeners : {
26416                     resize : function(r, w, h) {
26417                         _t.onResize(w,h); // -something
26418                     }
26419                 }
26420             });
26421             
26422         }
26423         this.createToolbar(this);
26424        
26425         
26426         if(!this.width){
26427             this.setSize(this.wrap.getSize());
26428         }
26429         if (this.resizeEl) {
26430             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26431             // should trigger onReize..
26432         }
26433         
26434 //        if(this.autosave && this.w){
26435 //            this.autoSaveFn = setInterval(this.autosave, 1000);
26436 //        }
26437     },
26438
26439     // private
26440     onResize : function(w, h)
26441     {
26442         //Roo.log('resize: ' +w + ',' + h );
26443         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
26444         var ew = false;
26445         var eh = false;
26446         
26447         if(this.el ){
26448             if(typeof w == 'number'){
26449                 var aw = w - this.wrap.getFrameWidth('lr');
26450                 this.el.setWidth(this.adjustWidth('textarea', aw));
26451                 ew = aw;
26452             }
26453             if(typeof h == 'number'){
26454                 var tbh = 0;
26455                 for (var i =0; i < this.toolbars.length;i++) {
26456                     // fixme - ask toolbars for heights?
26457                     tbh += this.toolbars[i].tb.el.getHeight();
26458                     if (this.toolbars[i].footer) {
26459                         tbh += this.toolbars[i].footer.el.getHeight();
26460                     }
26461                 }
26462                 
26463                 
26464                 
26465                 
26466                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26467                 ah -= 5; // knock a few pixes off for look..
26468                 this.el.setHeight(this.adjustWidth('textarea', ah));
26469                 var eh = ah;
26470             }
26471         }
26472         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26473         this.editorcore.onResize(ew,eh);
26474         
26475     },
26476
26477     /**
26478      * Toggles the editor between standard and source edit mode.
26479      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26480      */
26481     toggleSourceEdit : function(sourceEditMode)
26482     {
26483         this.editorcore.toggleSourceEdit(sourceEditMode);
26484         
26485         if(this.editorcore.sourceEditMode){
26486             Roo.log('editor - showing textarea');
26487             
26488 //            Roo.log('in');
26489 //            Roo.log(this.syncValue());
26490             this.editorcore.syncValue();
26491             this.el.removeClass('x-hidden');
26492             this.el.dom.removeAttribute('tabIndex');
26493             this.el.focus();
26494         }else{
26495             Roo.log('editor - hiding textarea');
26496 //            Roo.log('out')
26497 //            Roo.log(this.pushValue()); 
26498             this.editorcore.pushValue();
26499             
26500             this.el.addClass('x-hidden');
26501             this.el.dom.setAttribute('tabIndex', -1);
26502             //this.deferFocus();
26503         }
26504          
26505         this.setSize(this.wrap.getSize());
26506         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26507     },
26508  
26509     // private (for BoxComponent)
26510     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26511
26512     // private (for BoxComponent)
26513     getResizeEl : function(){
26514         return this.wrap;
26515     },
26516
26517     // private (for BoxComponent)
26518     getPositionEl : function(){
26519         return this.wrap;
26520     },
26521
26522     // private
26523     initEvents : function(){
26524         this.originalValue = this.getValue();
26525     },
26526
26527     /**
26528      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26529      * @method
26530      */
26531     markInvalid : Roo.emptyFn,
26532     /**
26533      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26534      * @method
26535      */
26536     clearInvalid : Roo.emptyFn,
26537
26538     setValue : function(v){
26539         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
26540         this.editorcore.pushValue();
26541     },
26542
26543      
26544     // private
26545     deferFocus : function(){
26546         this.focus.defer(10, this);
26547     },
26548
26549     // doc'ed in Field
26550     focus : function(){
26551         this.editorcore.focus();
26552         
26553     },
26554       
26555
26556     // private
26557     onDestroy : function(){
26558         
26559         
26560         
26561         if(this.rendered){
26562             
26563             for (var i =0; i < this.toolbars.length;i++) {
26564                 // fixme - ask toolbars for heights?
26565                 this.toolbars[i].onDestroy();
26566             }
26567             
26568             this.wrap.dom.innerHTML = '';
26569             this.wrap.remove();
26570         }
26571     },
26572
26573     // private
26574     onFirstFocus : function(){
26575         //Roo.log("onFirstFocus");
26576         this.editorcore.onFirstFocus();
26577          for (var i =0; i < this.toolbars.length;i++) {
26578             this.toolbars[i].onFirstFocus();
26579         }
26580         
26581     },
26582     
26583     // private
26584     syncValue : function()
26585     {
26586         this.editorcore.syncValue();
26587     },
26588     
26589     pushValue : function()
26590     {
26591         this.editorcore.pushValue();
26592     }
26593      
26594     
26595     // hide stuff that is not compatible
26596     /**
26597      * @event blur
26598      * @hide
26599      */
26600     /**
26601      * @event change
26602      * @hide
26603      */
26604     /**
26605      * @event focus
26606      * @hide
26607      */
26608     /**
26609      * @event specialkey
26610      * @hide
26611      */
26612     /**
26613      * @cfg {String} fieldClass @hide
26614      */
26615     /**
26616      * @cfg {String} focusClass @hide
26617      */
26618     /**
26619      * @cfg {String} autoCreate @hide
26620      */
26621     /**
26622      * @cfg {String} inputType @hide
26623      */
26624     /**
26625      * @cfg {String} invalidClass @hide
26626      */
26627     /**
26628      * @cfg {String} invalidText @hide
26629      */
26630     /**
26631      * @cfg {String} msgFx @hide
26632      */
26633     /**
26634      * @cfg {String} validateOnBlur @hide
26635      */
26636 });
26637  
26638     // <script type="text/javascript">
26639 /*
26640  * Based on
26641  * Ext JS Library 1.1.1
26642  * Copyright(c) 2006-2007, Ext JS, LLC.
26643  *  
26644  
26645  */
26646
26647 /**
26648  * @class Roo.form.HtmlEditorToolbar1
26649  * Basic Toolbar
26650  * 
26651  * Usage:
26652  *
26653  new Roo.form.HtmlEditor({
26654     ....
26655     toolbars : [
26656         new Roo.form.HtmlEditorToolbar1({
26657             disable : { fonts: 1 , format: 1, ..., ... , ...],
26658             btns : [ .... ]
26659         })
26660     }
26661      
26662  * 
26663  * @cfg {Object} disable List of elements to disable..
26664  * @cfg {Array} btns List of additional buttons.
26665  * 
26666  * 
26667  * NEEDS Extra CSS? 
26668  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26669  */
26670  
26671 Roo.form.HtmlEditor.ToolbarStandard = function(config)
26672 {
26673     
26674     Roo.apply(this, config);
26675     
26676     // default disabled, based on 'good practice'..
26677     this.disable = this.disable || {};
26678     Roo.applyIf(this.disable, {
26679         fontSize : true,
26680         colors : true,
26681         specialElements : true
26682     });
26683     
26684     
26685     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26686     // dont call parent... till later.
26687 }
26688
26689 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
26690     
26691     tb: false,
26692     
26693     rendered: false,
26694     
26695     editor : false,
26696     editorcore : false,
26697     /**
26698      * @cfg {Object} disable  List of toolbar elements to disable
26699          
26700      */
26701     disable : false,
26702     
26703     
26704      /**
26705      * @cfg {String} createLinkText The default text for the create link prompt
26706      */
26707     createLinkText : 'Please enter the URL for the link:',
26708     /**
26709      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
26710      */
26711     defaultLinkValue : 'http:/'+'/',
26712    
26713     
26714       /**
26715      * @cfg {Array} fontFamilies An array of available font families
26716      */
26717     fontFamilies : [
26718         'Arial',
26719         'Courier New',
26720         'Tahoma',
26721         'Times New Roman',
26722         'Verdana'
26723     ],
26724     
26725     specialChars : [
26726            "&#169;",
26727           "&#174;",     
26728           "&#8482;",    
26729           "&#163;" ,    
26730          // "&#8212;",    
26731           "&#8230;",    
26732           "&#247;" ,    
26733         //  "&#225;" ,     ?? a acute?
26734            "&#8364;"    , //Euro
26735        //   "&#8220;"    ,
26736         //  "&#8221;"    ,
26737         //  "&#8226;"    ,
26738           "&#176;"  //   , // degrees
26739
26740          // "&#233;"     , // e ecute
26741          // "&#250;"     , // u ecute?
26742     ],
26743     
26744     specialElements : [
26745         {
26746             text: "Insert Table",
26747             xtype: 'MenuItem',
26748             xns : Roo.Menu,
26749             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
26750                 
26751         },
26752         {    
26753             text: "Insert Image",
26754             xtype: 'MenuItem',
26755             xns : Roo.Menu,
26756             ihtml : '<img src="about:blank"/>'
26757             
26758         }
26759         
26760          
26761     ],
26762     
26763     
26764     inputElements : [ 
26765             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
26766             "input:submit", "input:button", "select", "textarea", "label" ],
26767     formats : [
26768         ["p"] ,  
26769         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
26770         ["pre"],[ "code"], 
26771         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
26772         ['div'],['span']
26773     ],
26774     
26775     cleanStyles : [
26776         "font-size"
26777     ],
26778      /**
26779      * @cfg {String} defaultFont default font to use.
26780      */
26781     defaultFont: 'tahoma',
26782    
26783     fontSelect : false,
26784     
26785     
26786     formatCombo : false,
26787     
26788     init : function(editor)
26789     {
26790         this.editor = editor;
26791         this.editorcore = editor.editorcore ? editor.editorcore : editor;
26792         var editorcore = this.editorcore;
26793         
26794         var _t = this;
26795         
26796         var fid = editorcore.frameId;
26797         var etb = this;
26798         function btn(id, toggle, handler){
26799             var xid = fid + '-'+ id ;
26800             return {
26801                 id : xid,
26802                 cmd : id,
26803                 cls : 'x-btn-icon x-edit-'+id,
26804                 enableToggle:toggle !== false,
26805                 scope: _t, // was editor...
26806                 handler:handler||_t.relayBtnCmd,
26807                 clickEvent:'mousedown',
26808                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26809                 tabIndex:-1
26810             };
26811         }
26812         
26813         
26814         
26815         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
26816         this.tb = tb;
26817          // stop form submits
26818         tb.el.on('click', function(e){
26819             e.preventDefault(); // what does this do?
26820         });
26821
26822         if(!this.disable.font) { // && !Roo.isSafari){
26823             /* why no safari for fonts 
26824             editor.fontSelect = tb.el.createChild({
26825                 tag:'select',
26826                 tabIndex: -1,
26827                 cls:'x-font-select',
26828                 html: this.createFontOptions()
26829             });
26830             
26831             editor.fontSelect.on('change', function(){
26832                 var font = editor.fontSelect.dom.value;
26833                 editor.relayCmd('fontname', font);
26834                 editor.deferFocus();
26835             }, editor);
26836             
26837             tb.add(
26838                 editor.fontSelect.dom,
26839                 '-'
26840             );
26841             */
26842             
26843         };
26844         if(!this.disable.formats){
26845             this.formatCombo = new Roo.form.ComboBox({
26846                 store: new Roo.data.SimpleStore({
26847                     id : 'tag',
26848                     fields: ['tag'],
26849                     data : this.formats // from states.js
26850                 }),
26851                 blockFocus : true,
26852                 name : '',
26853                 //autoCreate : {tag: "div",  size: "20"},
26854                 displayField:'tag',
26855                 typeAhead: false,
26856                 mode: 'local',
26857                 editable : false,
26858                 triggerAction: 'all',
26859                 emptyText:'Add tag',
26860                 selectOnFocus:true,
26861                 width:135,
26862                 listeners : {
26863                     'select': function(c, r, i) {
26864                         editorcore.insertTag(r.get('tag'));
26865                         editor.focus();
26866                     }
26867                 }
26868
26869             });
26870             tb.addField(this.formatCombo);
26871             
26872         }
26873         
26874         if(!this.disable.format){
26875             tb.add(
26876                 btn('bold'),
26877                 btn('italic'),
26878                 btn('underline')
26879             );
26880         };
26881         if(!this.disable.fontSize){
26882             tb.add(
26883                 '-',
26884                 
26885                 
26886                 btn('increasefontsize', false, editorcore.adjustFont),
26887                 btn('decreasefontsize', false, editorcore.adjustFont)
26888             );
26889         };
26890         
26891         
26892         if(!this.disable.colors){
26893             tb.add(
26894                 '-', {
26895                     id:editorcore.frameId +'-forecolor',
26896                     cls:'x-btn-icon x-edit-forecolor',
26897                     clickEvent:'mousedown',
26898                     tooltip: this.buttonTips['forecolor'] || undefined,
26899                     tabIndex:-1,
26900                     menu : new Roo.menu.ColorMenu({
26901                         allowReselect: true,
26902                         focus: Roo.emptyFn,
26903                         value:'000000',
26904                         plain:true,
26905                         selectHandler: function(cp, color){
26906                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
26907                             editor.deferFocus();
26908                         },
26909                         scope: editorcore,
26910                         clickEvent:'mousedown'
26911                     })
26912                 }, {
26913                     id:editorcore.frameId +'backcolor',
26914                     cls:'x-btn-icon x-edit-backcolor',
26915                     clickEvent:'mousedown',
26916                     tooltip: this.buttonTips['backcolor'] || undefined,
26917                     tabIndex:-1,
26918                     menu : new Roo.menu.ColorMenu({
26919                         focus: Roo.emptyFn,
26920                         value:'FFFFFF',
26921                         plain:true,
26922                         allowReselect: true,
26923                         selectHandler: function(cp, color){
26924                             if(Roo.isGecko){
26925                                 editorcore.execCmd('useCSS', false);
26926                                 editorcore.execCmd('hilitecolor', color);
26927                                 editorcore.execCmd('useCSS', true);
26928                                 editor.deferFocus();
26929                             }else{
26930                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
26931                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
26932                                 editor.deferFocus();
26933                             }
26934                         },
26935                         scope:editorcore,
26936                         clickEvent:'mousedown'
26937                     })
26938                 }
26939             );
26940         };
26941         // now add all the items...
26942         
26943
26944         if(!this.disable.alignments){
26945             tb.add(
26946                 '-',
26947                 btn('justifyleft'),
26948                 btn('justifycenter'),
26949                 btn('justifyright')
26950             );
26951         };
26952
26953         //if(!Roo.isSafari){
26954             if(!this.disable.links){
26955                 tb.add(
26956                     '-',
26957                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
26958                 );
26959             };
26960
26961             if(!this.disable.lists){
26962                 tb.add(
26963                     '-',
26964                     btn('insertorderedlist'),
26965                     btn('insertunorderedlist')
26966                 );
26967             }
26968             if(!this.disable.sourceEdit){
26969                 tb.add(
26970                     '-',
26971                     btn('sourceedit', true, function(btn){
26972                         Roo.log(this);
26973                         this.toggleSourceEdit(btn.pressed);
26974                     })
26975                 );
26976             }
26977         //}
26978         
26979         var smenu = { };
26980         // special menu.. - needs to be tidied up..
26981         if (!this.disable.special) {
26982             smenu = {
26983                 text: "&#169;",
26984                 cls: 'x-edit-none',
26985                 
26986                 menu : {
26987                     items : []
26988                 }
26989             };
26990             for (var i =0; i < this.specialChars.length; i++) {
26991                 smenu.menu.items.push({
26992                     
26993                     html: this.specialChars[i],
26994                     handler: function(a,b) {
26995                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
26996                         //editor.insertAtCursor(a.html);
26997                         
26998                     },
26999                     tabIndex:-1
27000                 });
27001             }
27002             
27003             
27004             tb.add(smenu);
27005             
27006             
27007         }
27008         
27009         var cmenu = { };
27010         if (!this.disable.cleanStyles) {
27011             cmenu = {
27012                 cls: 'x-btn-icon x-btn-clear',
27013                 
27014                 menu : {
27015                     items : []
27016                 }
27017             };
27018             for (var i =0; i < this.cleanStyles.length; i++) {
27019                 cmenu.menu.items.push({
27020                     actiontype : this.cleanStyles[i],
27021                     html: 'Remove ' + this.cleanStyles[i],
27022                     handler: function(a,b) {
27023                         Roo.log(a);
27024                         Roo.log(b);
27025                         var c = Roo.get(editorcore.doc.body);
27026                         c.select('[style]').each(function(s) {
27027                             s.dom.style.removeProperty(a.actiontype);
27028                         });
27029                         editorcore.syncValue();
27030                     },
27031                     tabIndex:-1
27032                 });
27033             }
27034             cmenu.menu.items.push({
27035                 actiontype : 'word',
27036                 html: 'Remove MS Word Formating',
27037                 handler: function(a,b) {
27038                     editorcore.cleanWord();
27039                     editorcore.syncValue();
27040                 },
27041                 tabIndex:-1
27042             });
27043             
27044             cmenu.menu.items.push({
27045                 actiontype : 'all',
27046                 html: 'Remove All Styles',
27047                 handler: function(a,b) {
27048                     
27049                     var c = Roo.get(editorcore.doc.body);
27050                     c.select('[style]').each(function(s) {
27051                         s.dom.removeAttribute('style');
27052                     });
27053                     editorcore.syncValue();
27054                 },
27055                 tabIndex:-1
27056             });
27057              cmenu.menu.items.push({
27058                 actiontype : 'word',
27059                 html: 'Tidy HTML Source',
27060                 handler: function(a,b) {
27061                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
27062                     editorcore.syncValue();
27063                 },
27064                 tabIndex:-1
27065             });
27066             
27067             
27068             tb.add(cmenu);
27069         }
27070          
27071         if (!this.disable.specialElements) {
27072             var semenu = {
27073                 text: "Other;",
27074                 cls: 'x-edit-none',
27075                 menu : {
27076                     items : []
27077                 }
27078             };
27079             for (var i =0; i < this.specialElements.length; i++) {
27080                 semenu.menu.items.push(
27081                     Roo.apply({ 
27082                         handler: function(a,b) {
27083                             editor.insertAtCursor(this.ihtml);
27084                         }
27085                     }, this.specialElements[i])
27086                 );
27087                     
27088             }
27089             
27090             tb.add(semenu);
27091             
27092             
27093         }
27094          
27095         
27096         if (this.btns) {
27097             for(var i =0; i< this.btns.length;i++) {
27098                 var b = Roo.factory(this.btns[i],Roo.form);
27099                 b.cls =  'x-edit-none';
27100                 b.scope = editorcore;
27101                 tb.add(b);
27102             }
27103         
27104         }
27105         
27106         
27107         
27108         // disable everything...
27109         
27110         this.tb.items.each(function(item){
27111            if(item.id != editorcore.frameId+ '-sourceedit'){
27112                 item.disable();
27113             }
27114         });
27115         this.rendered = true;
27116         
27117         // the all the btns;
27118         editor.on('editorevent', this.updateToolbar, this);
27119         // other toolbars need to implement this..
27120         //editor.on('editmodechange', this.updateToolbar, this);
27121     },
27122     
27123     
27124     relayBtnCmd : function(btn) {
27125         this.editorcore.relayCmd(btn.cmd);
27126     },
27127     // private used internally
27128     createLink : function(){
27129         Roo.log("create link?");
27130         var url = prompt(this.createLinkText, this.defaultLinkValue);
27131         if(url && url != 'http:/'+'/'){
27132             this.editorcore.relayCmd('createlink', url);
27133         }
27134     },
27135
27136     
27137     /**
27138      * Protected method that will not generally be called directly. It triggers
27139      * a toolbar update by reading the markup state of the current selection in the editor.
27140      */
27141     updateToolbar: function(){
27142
27143         if(!this.editorcore.activated){
27144             this.editor.onFirstFocus();
27145             return;
27146         }
27147
27148         var btns = this.tb.items.map, 
27149             doc = this.editorcore.doc,
27150             frameId = this.editorcore.frameId;
27151
27152         if(!this.disable.font && !Roo.isSafari){
27153             /*
27154             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
27155             if(name != this.fontSelect.dom.value){
27156                 this.fontSelect.dom.value = name;
27157             }
27158             */
27159         }
27160         if(!this.disable.format){
27161             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
27162             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
27163             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
27164         }
27165         if(!this.disable.alignments){
27166             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
27167             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
27168             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
27169         }
27170         if(!Roo.isSafari && !this.disable.lists){
27171             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
27172             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
27173         }
27174         
27175         var ans = this.editorcore.getAllAncestors();
27176         if (this.formatCombo) {
27177             
27178             
27179             var store = this.formatCombo.store;
27180             this.formatCombo.setValue("");
27181             for (var i =0; i < ans.length;i++) {
27182                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27183                     // select it..
27184                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27185                     break;
27186                 }
27187             }
27188         }
27189         
27190         
27191         
27192         // hides menus... - so this cant be on a menu...
27193         Roo.menu.MenuMgr.hideAll();
27194
27195         //this.editorsyncValue();
27196     },
27197    
27198     
27199     createFontOptions : function(){
27200         var buf = [], fs = this.fontFamilies, ff, lc;
27201         
27202         
27203         
27204         for(var i = 0, len = fs.length; i< len; i++){
27205             ff = fs[i];
27206             lc = ff.toLowerCase();
27207             buf.push(
27208                 '<option value="',lc,'" style="font-family:',ff,';"',
27209                     (this.defaultFont == lc ? ' selected="true">' : '>'),
27210                     ff,
27211                 '</option>'
27212             );
27213         }
27214         return buf.join('');
27215     },
27216     
27217     toggleSourceEdit : function(sourceEditMode){
27218         
27219         Roo.log("toolbar toogle");
27220         if(sourceEditMode === undefined){
27221             sourceEditMode = !this.sourceEditMode;
27222         }
27223         this.sourceEditMode = sourceEditMode === true;
27224         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
27225         // just toggle the button?
27226         if(btn.pressed !== this.sourceEditMode){
27227             btn.toggle(this.sourceEditMode);
27228             return;
27229         }
27230         
27231         if(sourceEditMode){
27232             Roo.log("disabling buttons");
27233             this.tb.items.each(function(item){
27234                 if(item.cmd != 'sourceedit'){
27235                     item.disable();
27236                 }
27237             });
27238           
27239         }else{
27240             Roo.log("enabling buttons");
27241             if(this.editorcore.initialized){
27242                 this.tb.items.each(function(item){
27243                     item.enable();
27244                 });
27245             }
27246             
27247         }
27248         Roo.log("calling toggole on editor");
27249         // tell the editor that it's been pressed..
27250         this.editor.toggleSourceEdit(sourceEditMode);
27251        
27252     },
27253      /**
27254      * Object collection of toolbar tooltips for the buttons in the editor. The key
27255      * is the command id associated with that button and the value is a valid QuickTips object.
27256      * For example:
27257 <pre><code>
27258 {
27259     bold : {
27260         title: 'Bold (Ctrl+B)',
27261         text: 'Make the selected text bold.',
27262         cls: 'x-html-editor-tip'
27263     },
27264     italic : {
27265         title: 'Italic (Ctrl+I)',
27266         text: 'Make the selected text italic.',
27267         cls: 'x-html-editor-tip'
27268     },
27269     ...
27270 </code></pre>
27271     * @type Object
27272      */
27273     buttonTips : {
27274         bold : {
27275             title: 'Bold (Ctrl+B)',
27276             text: 'Make the selected text bold.',
27277             cls: 'x-html-editor-tip'
27278         },
27279         italic : {
27280             title: 'Italic (Ctrl+I)',
27281             text: 'Make the selected text italic.',
27282             cls: 'x-html-editor-tip'
27283         },
27284         underline : {
27285             title: 'Underline (Ctrl+U)',
27286             text: 'Underline the selected text.',
27287             cls: 'x-html-editor-tip'
27288         },
27289         increasefontsize : {
27290             title: 'Grow Text',
27291             text: 'Increase the font size.',
27292             cls: 'x-html-editor-tip'
27293         },
27294         decreasefontsize : {
27295             title: 'Shrink Text',
27296             text: 'Decrease the font size.',
27297             cls: 'x-html-editor-tip'
27298         },
27299         backcolor : {
27300             title: 'Text Highlight Color',
27301             text: 'Change the background color of the selected text.',
27302             cls: 'x-html-editor-tip'
27303         },
27304         forecolor : {
27305             title: 'Font Color',
27306             text: 'Change the color of the selected text.',
27307             cls: 'x-html-editor-tip'
27308         },
27309         justifyleft : {
27310             title: 'Align Text Left',
27311             text: 'Align text to the left.',
27312             cls: 'x-html-editor-tip'
27313         },
27314         justifycenter : {
27315             title: 'Center Text',
27316             text: 'Center text in the editor.',
27317             cls: 'x-html-editor-tip'
27318         },
27319         justifyright : {
27320             title: 'Align Text Right',
27321             text: 'Align text to the right.',
27322             cls: 'x-html-editor-tip'
27323         },
27324         insertunorderedlist : {
27325             title: 'Bullet List',
27326             text: 'Start a bulleted list.',
27327             cls: 'x-html-editor-tip'
27328         },
27329         insertorderedlist : {
27330             title: 'Numbered List',
27331             text: 'Start a numbered list.',
27332             cls: 'x-html-editor-tip'
27333         },
27334         createlink : {
27335             title: 'Hyperlink',
27336             text: 'Make the selected text a hyperlink.',
27337             cls: 'x-html-editor-tip'
27338         },
27339         sourceedit : {
27340             title: 'Source Edit',
27341             text: 'Switch to source editing mode.',
27342             cls: 'x-html-editor-tip'
27343         }
27344     },
27345     // private
27346     onDestroy : function(){
27347         if(this.rendered){
27348             
27349             this.tb.items.each(function(item){
27350                 if(item.menu){
27351                     item.menu.removeAll();
27352                     if(item.menu.el){
27353                         item.menu.el.destroy();
27354                     }
27355                 }
27356                 item.destroy();
27357             });
27358              
27359         }
27360     },
27361     onFirstFocus: function() {
27362         this.tb.items.each(function(item){
27363            item.enable();
27364         });
27365     }
27366 });
27367
27368
27369
27370
27371 // <script type="text/javascript">
27372 /*
27373  * Based on
27374  * Ext JS Library 1.1.1
27375  * Copyright(c) 2006-2007, Ext JS, LLC.
27376  *  
27377  
27378  */
27379
27380  
27381 /**
27382  * @class Roo.form.HtmlEditor.ToolbarContext
27383  * Context Toolbar
27384  * 
27385  * Usage:
27386  *
27387  new Roo.form.HtmlEditor({
27388     ....
27389     toolbars : [
27390         { xtype: 'ToolbarStandard', styles : {} }
27391         { xtype: 'ToolbarContext', disable : {} }
27392     ]
27393 })
27394
27395      
27396  * 
27397  * @config : {Object} disable List of elements to disable.. (not done yet.)
27398  * @config : {Object} styles  Map of styles available.
27399  * 
27400  */
27401
27402 Roo.form.HtmlEditor.ToolbarContext = function(config)
27403 {
27404     
27405     Roo.apply(this, config);
27406     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27407     // dont call parent... till later.
27408     this.styles = this.styles || {};
27409 }
27410
27411  
27412
27413 Roo.form.HtmlEditor.ToolbarContext.types = {
27414     'IMG' : {
27415         width : {
27416             title: "Width",
27417             width: 40
27418         },
27419         height:  {
27420             title: "Height",
27421             width: 40
27422         },
27423         align: {
27424             title: "Align",
27425             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
27426             width : 80
27427             
27428         },
27429         border: {
27430             title: "Border",
27431             width: 40
27432         },
27433         alt: {
27434             title: "Alt",
27435             width: 120
27436         },
27437         src : {
27438             title: "Src",
27439             width: 220
27440         }
27441         
27442     },
27443     'A' : {
27444         name : {
27445             title: "Name",
27446             width: 50
27447         },
27448         target:  {
27449             title: "Target",
27450             width: 120
27451         },
27452         href:  {
27453             title: "Href",
27454             width: 220
27455         } // border?
27456         
27457     },
27458     'TABLE' : {
27459         rows : {
27460             title: "Rows",
27461             width: 20
27462         },
27463         cols : {
27464             title: "Cols",
27465             width: 20
27466         },
27467         width : {
27468             title: "Width",
27469             width: 40
27470         },
27471         height : {
27472             title: "Height",
27473             width: 40
27474         },
27475         border : {
27476             title: "Border",
27477             width: 20
27478         }
27479     },
27480     'TD' : {
27481         width : {
27482             title: "Width",
27483             width: 40
27484         },
27485         height : {
27486             title: "Height",
27487             width: 40
27488         },   
27489         align: {
27490             title: "Align",
27491             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27492             width: 80
27493         },
27494         valign: {
27495             title: "Valign",
27496             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27497             width: 80
27498         },
27499         colspan: {
27500             title: "Colspan",
27501             width: 20
27502             
27503         },
27504          'font-family'  : {
27505             title : "Font",
27506             style : 'fontFamily',
27507             displayField: 'display',
27508             optname : 'font-family',
27509             width: 140
27510         }
27511     },
27512     'INPUT' : {
27513         name : {
27514             title: "name",
27515             width: 120
27516         },
27517         value : {
27518             title: "Value",
27519             width: 120
27520         },
27521         width : {
27522             title: "Width",
27523             width: 40
27524         }
27525     },
27526     'LABEL' : {
27527         'for' : {
27528             title: "For",
27529             width: 120
27530         }
27531     },
27532     'TEXTAREA' : {
27533           name : {
27534             title: "name",
27535             width: 120
27536         },
27537         rows : {
27538             title: "Rows",
27539             width: 20
27540         },
27541         cols : {
27542             title: "Cols",
27543             width: 20
27544         }
27545     },
27546     'SELECT' : {
27547         name : {
27548             title: "name",
27549             width: 120
27550         },
27551         selectoptions : {
27552             title: "Options",
27553             width: 200
27554         }
27555     },
27556     
27557     // should we really allow this??
27558     // should this just be 
27559     'BODY' : {
27560         title : {
27561             title: "Title",
27562             width: 200,
27563             disabled : true
27564         }
27565     },
27566     'SPAN' : {
27567         'font-family'  : {
27568             title : "Font",
27569             style : 'fontFamily',
27570             displayField: 'display',
27571             optname : 'font-family',
27572             width: 140
27573         }
27574     },
27575     'DIV' : {
27576         'font-family'  : {
27577             title : "Font",
27578             style : 'fontFamily',
27579             displayField: 'display',
27580             optname : 'font-family',
27581             width: 140
27582         }
27583     },
27584      'P' : {
27585         'font-family'  : {
27586             title : "Font",
27587             style : 'fontFamily',
27588             displayField: 'display',
27589             optname : 'font-family',
27590             width: 140
27591         }
27592     },
27593     
27594     '*' : {
27595         // empty..
27596     }
27597
27598 };
27599
27600 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
27601 Roo.form.HtmlEditor.ToolbarContext.stores = false;
27602
27603 Roo.form.HtmlEditor.ToolbarContext.options = {
27604         'font-family'  : [ 
27605                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
27606                 [ 'Courier New', 'Courier New'],
27607                 [ 'Tahoma', 'Tahoma'],
27608                 [ 'Times New Roman,serif', 'Times'],
27609                 [ 'Verdana','Verdana' ]
27610         ]
27611 };
27612
27613 // fixme - these need to be configurable..
27614  
27615
27616 Roo.form.HtmlEditor.ToolbarContext.types
27617
27618
27619 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
27620     
27621     tb: false,
27622     
27623     rendered: false,
27624     
27625     editor : false,
27626     editorcore : false,
27627     /**
27628      * @cfg {Object} disable  List of toolbar elements to disable
27629          
27630      */
27631     disable : false,
27632     /**
27633      * @cfg {Object} styles List of styles 
27634      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
27635      *
27636      * These must be defined in the page, so they get rendered correctly..
27637      * .headline { }
27638      * TD.underline { }
27639      * 
27640      */
27641     styles : false,
27642     
27643     options: false,
27644     
27645     toolbars : false,
27646     
27647     init : function(editor)
27648     {
27649         this.editor = editor;
27650         this.editorcore = editor.editorcore ? editor.editorcore : editor;
27651         var editorcore = this.editorcore;
27652         
27653         var fid = editorcore.frameId;
27654         var etb = this;
27655         function btn(id, toggle, handler){
27656             var xid = fid + '-'+ id ;
27657             return {
27658                 id : xid,
27659                 cmd : id,
27660                 cls : 'x-btn-icon x-edit-'+id,
27661                 enableToggle:toggle !== false,
27662                 scope: editorcore, // was editor...
27663                 handler:handler||editorcore.relayBtnCmd,
27664                 clickEvent:'mousedown',
27665                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27666                 tabIndex:-1
27667             };
27668         }
27669         // create a new element.
27670         var wdiv = editor.wrap.createChild({
27671                 tag: 'div'
27672             }, editor.wrap.dom.firstChild.nextSibling, true);
27673         
27674         // can we do this more than once??
27675         
27676          // stop form submits
27677       
27678  
27679         // disable everything...
27680         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27681         this.toolbars = {};
27682            
27683         for (var i in  ty) {
27684           
27685             this.toolbars[i] = this.buildToolbar(ty[i],i);
27686         }
27687         this.tb = this.toolbars.BODY;
27688         this.tb.el.show();
27689         this.buildFooter();
27690         this.footer.show();
27691         editor.on('hide', function( ) { this.footer.hide() }, this);
27692         editor.on('show', function( ) { this.footer.show() }, this);
27693         
27694          
27695         this.rendered = true;
27696         
27697         // the all the btns;
27698         editor.on('editorevent', this.updateToolbar, this);
27699         // other toolbars need to implement this..
27700         //editor.on('editmodechange', this.updateToolbar, this);
27701     },
27702     
27703     
27704     
27705     /**
27706      * Protected method that will not generally be called directly. It triggers
27707      * a toolbar update by reading the markup state of the current selection in the editor.
27708      */
27709     updateToolbar: function(editor,ev,sel){
27710
27711         //Roo.log(ev);
27712         // capture mouse up - this is handy for selecting images..
27713         // perhaps should go somewhere else...
27714         if(!this.editorcore.activated){
27715              this.editor.onFirstFocus();
27716             return;
27717         }
27718         
27719         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
27720         // selectNode - might want to handle IE?
27721         if (ev &&
27722             (ev.type == 'mouseup' || ev.type == 'click' ) &&
27723             ev.target && ev.target.tagName == 'IMG') {
27724             // they have click on an image...
27725             // let's see if we can change the selection...
27726             sel = ev.target;
27727          
27728               var nodeRange = sel.ownerDocument.createRange();
27729             try {
27730                 nodeRange.selectNode(sel);
27731             } catch (e) {
27732                 nodeRange.selectNodeContents(sel);
27733             }
27734             //nodeRange.collapse(true);
27735             var s = this.editorcore.win.getSelection();
27736             s.removeAllRanges();
27737             s.addRange(nodeRange);
27738         }  
27739         
27740       
27741         var updateFooter = sel ? false : true;
27742         
27743         
27744         var ans = this.editorcore.getAllAncestors();
27745         
27746         // pick
27747         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27748         
27749         if (!sel) { 
27750             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
27751             sel = sel ? sel : this.editorcore.doc.body;
27752             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
27753             
27754         }
27755         // pick a menu that exists..
27756         var tn = sel.tagName.toUpperCase();
27757         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
27758         
27759         tn = sel.tagName.toUpperCase();
27760         
27761         var lastSel = this.tb.selectedNode
27762         
27763         this.tb.selectedNode = sel;
27764         
27765         // if current menu does not match..
27766         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
27767                 
27768             this.tb.el.hide();
27769             ///console.log("show: " + tn);
27770             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
27771             this.tb.el.show();
27772             // update name
27773             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
27774             
27775             
27776             // update attributes
27777             if (this.tb.fields) {
27778                 this.tb.fields.each(function(e) {
27779                     if (e.stylename) {
27780                         e.setValue(sel.style[e.stylename]);
27781                         return;
27782                     } 
27783                    e.setValue(sel.getAttribute(e.attrname));
27784                 });
27785             }
27786             
27787             var hasStyles = false;
27788             for(var i in this.styles) {
27789                 hasStyles = true;
27790                 break;
27791             }
27792             
27793             // update styles
27794             if (hasStyles) { 
27795                 var st = this.tb.fields.item(0);
27796                 
27797                 st.store.removeAll();
27798                
27799                 
27800                 var cn = sel.className.split(/\s+/);
27801                 
27802                 var avs = [];
27803                 if (this.styles['*']) {
27804                     
27805                     Roo.each(this.styles['*'], function(v) {
27806                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27807                     });
27808                 }
27809                 if (this.styles[tn]) { 
27810                     Roo.each(this.styles[tn], function(v) {
27811                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27812                     });
27813                 }
27814                 
27815                 st.store.loadData(avs);
27816                 st.collapse();
27817                 st.setValue(cn);
27818             }
27819             // flag our selected Node.
27820             this.tb.selectedNode = sel;
27821            
27822            
27823             Roo.menu.MenuMgr.hideAll();
27824
27825         }
27826         
27827         if (!updateFooter) {
27828             //this.footDisp.dom.innerHTML = ''; 
27829             return;
27830         }
27831         // update the footer
27832         //
27833         var html = '';
27834         
27835         this.footerEls = ans.reverse();
27836         Roo.each(this.footerEls, function(a,i) {
27837             if (!a) { return; }
27838             html += html.length ? ' &gt; '  :  '';
27839             
27840             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
27841             
27842         });
27843        
27844         // 
27845         var sz = this.footDisp.up('td').getSize();
27846         this.footDisp.dom.style.width = (sz.width -10) + 'px';
27847         this.footDisp.dom.style.marginLeft = '5px';
27848         
27849         this.footDisp.dom.style.overflow = 'hidden';
27850         
27851         this.footDisp.dom.innerHTML = html;
27852             
27853         //this.editorsyncValue();
27854     },
27855      
27856     
27857    
27858        
27859     // private
27860     onDestroy : function(){
27861         if(this.rendered){
27862             
27863             this.tb.items.each(function(item){
27864                 if(item.menu){
27865                     item.menu.removeAll();
27866                     if(item.menu.el){
27867                         item.menu.el.destroy();
27868                     }
27869                 }
27870                 item.destroy();
27871             });
27872              
27873         }
27874     },
27875     onFirstFocus: function() {
27876         // need to do this for all the toolbars..
27877         this.tb.items.each(function(item){
27878            item.enable();
27879         });
27880     },
27881     buildToolbar: function(tlist, nm)
27882     {
27883         var editor = this.editor;
27884         var editorcore = this.editorcore;
27885          // create a new element.
27886         var wdiv = editor.wrap.createChild({
27887                 tag: 'div'
27888             }, editor.wrap.dom.firstChild.nextSibling, true);
27889         
27890        
27891         var tb = new Roo.Toolbar(wdiv);
27892         // add the name..
27893         
27894         tb.add(nm+ ":&nbsp;");
27895         
27896         var styles = [];
27897         for(var i in this.styles) {
27898             styles.push(i);
27899         }
27900         
27901         // styles...
27902         if (styles && styles.length) {
27903             
27904             // this needs a multi-select checkbox...
27905             tb.addField( new Roo.form.ComboBox({
27906                 store: new Roo.data.SimpleStore({
27907                     id : 'val',
27908                     fields: ['val', 'selected'],
27909                     data : [] 
27910                 }),
27911                 name : '-roo-edit-className',
27912                 attrname : 'className',
27913                 displayField: 'val',
27914                 typeAhead: false,
27915                 mode: 'local',
27916                 editable : false,
27917                 triggerAction: 'all',
27918                 emptyText:'Select Style',
27919                 selectOnFocus:true,
27920                 width: 130,
27921                 listeners : {
27922                     'select': function(c, r, i) {
27923                         // initial support only for on class per el..
27924                         tb.selectedNode.className =  r ? r.get('val') : '';
27925                         editorcore.syncValue();
27926                     }
27927                 }
27928     
27929             }));
27930         }
27931         
27932         var tbc = Roo.form.HtmlEditor.ToolbarContext;
27933         var tbops = tbc.options;
27934         
27935         for (var i in tlist) {
27936             
27937             var item = tlist[i];
27938             tb.add(item.title + ":&nbsp;");
27939             
27940             
27941             //optname == used so you can configure the options available..
27942             var opts = item.opts ? item.opts : false;
27943             if (item.optname) {
27944                 opts = tbops[item.optname];
27945            
27946             }
27947             
27948             if (opts) {
27949                 // opts == pulldown..
27950                 tb.addField( new Roo.form.ComboBox({
27951                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
27952                         id : 'val',
27953                         fields: ['val', 'display'],
27954                         data : opts  
27955                     }),
27956                     name : '-roo-edit-' + i,
27957                     attrname : i,
27958                     stylename : item.style ? item.style : false,
27959                     displayField: item.displayField ? item.displayField : 'val',
27960                     valueField :  'val',
27961                     typeAhead: false,
27962                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
27963                     editable : false,
27964                     triggerAction: 'all',
27965                     emptyText:'Select',
27966                     selectOnFocus:true,
27967                     width: item.width ? item.width  : 130,
27968                     listeners : {
27969                         'select': function(c, r, i) {
27970                             if (c.stylename) {
27971                                 tb.selectedNode.style[c.stylename] =  r.get('val');
27972                                 return;
27973                             }
27974                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
27975                         }
27976                     }
27977
27978                 }));
27979                 continue;
27980                     
27981                  
27982                 
27983                 tb.addField( new Roo.form.TextField({
27984                     name: i,
27985                     width: 100,
27986                     //allowBlank:false,
27987                     value: ''
27988                 }));
27989                 continue;
27990             }
27991             tb.addField( new Roo.form.TextField({
27992                 name: '-roo-edit-' + i,
27993                 attrname : i,
27994                 
27995                 width: item.width,
27996                 //allowBlank:true,
27997                 value: '',
27998                 listeners: {
27999                     'change' : function(f, nv, ov) {
28000                         tb.selectedNode.setAttribute(f.attrname, nv);
28001                     }
28002                 }
28003             }));
28004              
28005         }
28006         tb.addFill();
28007         var _this = this;
28008         tb.addButton( {
28009             text: 'Remove Tag',
28010     
28011             listeners : {
28012                 click : function ()
28013                 {
28014                     // remove
28015                     // undo does not work.
28016                      
28017                     var sn = tb.selectedNode;
28018                     
28019                     var pn = sn.parentNode;
28020                     
28021                     var stn =  sn.childNodes[0];
28022                     var en = sn.childNodes[sn.childNodes.length - 1 ];
28023                     while (sn.childNodes.length) {
28024                         var node = sn.childNodes[0];
28025                         sn.removeChild(node);
28026                         //Roo.log(node);
28027                         pn.insertBefore(node, sn);
28028                         
28029                     }
28030                     pn.removeChild(sn);
28031                     var range = editorcore.createRange();
28032         
28033                     range.setStart(stn,0);
28034                     range.setEnd(en,0); //????
28035                     //range.selectNode(sel);
28036                     
28037                     
28038                     var selection = editorcore.getSelection();
28039                     selection.removeAllRanges();
28040                     selection.addRange(range);
28041                     
28042                     
28043                     
28044                     //_this.updateToolbar(null, null, pn);
28045                     _this.updateToolbar(null, null, null);
28046                     _this.footDisp.dom.innerHTML = ''; 
28047                 }
28048             }
28049             
28050                     
28051                 
28052             
28053         });
28054         
28055         
28056         tb.el.on('click', function(e){
28057             e.preventDefault(); // what does this do?
28058         });
28059         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
28060         tb.el.hide();
28061         tb.name = nm;
28062         // dont need to disable them... as they will get hidden
28063         return tb;
28064          
28065         
28066     },
28067     buildFooter : function()
28068     {
28069         
28070         var fel = this.editor.wrap.createChild();
28071         this.footer = new Roo.Toolbar(fel);
28072         // toolbar has scrolly on left / right?
28073         var footDisp= new Roo.Toolbar.Fill();
28074         var _t = this;
28075         this.footer.add(
28076             {
28077                 text : '&lt;',
28078                 xtype: 'Button',
28079                 handler : function() {
28080                     _t.footDisp.scrollTo('left',0,true)
28081                 }
28082             }
28083         );
28084         this.footer.add( footDisp );
28085         this.footer.add( 
28086             {
28087                 text : '&gt;',
28088                 xtype: 'Button',
28089                 handler : function() {
28090                     // no animation..
28091                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
28092                 }
28093             }
28094         );
28095         var fel = Roo.get(footDisp.el);
28096         fel.addClass('x-editor-context');
28097         this.footDispWrap = fel; 
28098         this.footDispWrap.overflow  = 'hidden';
28099         
28100         this.footDisp = fel.createChild();
28101         this.footDispWrap.on('click', this.onContextClick, this)
28102         
28103         
28104     },
28105     onContextClick : function (ev,dom)
28106     {
28107         ev.preventDefault();
28108         var  cn = dom.className;
28109         //Roo.log(cn);
28110         if (!cn.match(/x-ed-loc-/)) {
28111             return;
28112         }
28113         var n = cn.split('-').pop();
28114         var ans = this.footerEls;
28115         var sel = ans[n];
28116         
28117          // pick
28118         var range = this.editorcore.createRange();
28119         
28120         range.selectNodeContents(sel);
28121         //range.selectNode(sel);
28122         
28123         
28124         var selection = this.editorcore.getSelection();
28125         selection.removeAllRanges();
28126         selection.addRange(range);
28127         
28128         
28129         
28130         this.updateToolbar(null, null, sel);
28131         
28132         
28133     }
28134     
28135     
28136     
28137     
28138     
28139 });
28140
28141
28142
28143
28144
28145 /*
28146  * Based on:
28147  * Ext JS Library 1.1.1
28148  * Copyright(c) 2006-2007, Ext JS, LLC.
28149  *
28150  * Originally Released Under LGPL - original licence link has changed is not relivant.
28151  *
28152  * Fork - LGPL
28153  * <script type="text/javascript">
28154  */
28155  
28156 /**
28157  * @class Roo.form.BasicForm
28158  * @extends Roo.util.Observable
28159  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
28160  * @constructor
28161  * @param {String/HTMLElement/Roo.Element} el The form element or its id
28162  * @param {Object} config Configuration options
28163  */
28164 Roo.form.BasicForm = function(el, config){
28165     this.allItems = [];
28166     this.childForms = [];
28167     Roo.apply(this, config);
28168     /*
28169      * The Roo.form.Field items in this form.
28170      * @type MixedCollection
28171      */
28172      
28173      
28174     this.items = new Roo.util.MixedCollection(false, function(o){
28175         return o.id || (o.id = Roo.id());
28176     });
28177     this.addEvents({
28178         /**
28179          * @event beforeaction
28180          * Fires before any action is performed. Return false to cancel the action.
28181          * @param {Form} this
28182          * @param {Action} action The action to be performed
28183          */
28184         beforeaction: true,
28185         /**
28186          * @event actionfailed
28187          * Fires when an action fails.
28188          * @param {Form} this
28189          * @param {Action} action The action that failed
28190          */
28191         actionfailed : true,
28192         /**
28193          * @event actioncomplete
28194          * Fires when an action is completed.
28195          * @param {Form} this
28196          * @param {Action} action The action that completed
28197          */
28198         actioncomplete : true
28199     });
28200     if(el){
28201         this.initEl(el);
28202     }
28203     Roo.form.BasicForm.superclass.constructor.call(this);
28204 };
28205
28206 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
28207     /**
28208      * @cfg {String} method
28209      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
28210      */
28211     /**
28212      * @cfg {DataReader} reader
28213      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
28214      * This is optional as there is built-in support for processing JSON.
28215      */
28216     /**
28217      * @cfg {DataReader} errorReader
28218      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
28219      * This is completely optional as there is built-in support for processing JSON.
28220      */
28221     /**
28222      * @cfg {String} url
28223      * The URL to use for form actions if one isn't supplied in the action options.
28224      */
28225     /**
28226      * @cfg {Boolean} fileUpload
28227      * Set to true if this form is a file upload.
28228      */
28229      
28230     /**
28231      * @cfg {Object} baseParams
28232      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
28233      */
28234      /**
28235      
28236     /**
28237      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
28238      */
28239     timeout: 30,
28240
28241     // private
28242     activeAction : null,
28243
28244     /**
28245      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
28246      * or setValues() data instead of when the form was first created.
28247      */
28248     trackResetOnLoad : false,
28249     
28250     
28251     /**
28252      * childForms - used for multi-tab forms
28253      * @type {Array}
28254      */
28255     childForms : false,
28256     
28257     /**
28258      * allItems - full list of fields.
28259      * @type {Array}
28260      */
28261     allItems : false,
28262     
28263     /**
28264      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
28265      * element by passing it or its id or mask the form itself by passing in true.
28266      * @type Mixed
28267      */
28268     waitMsgTarget : false,
28269
28270     // private
28271     initEl : function(el){
28272         this.el = Roo.get(el);
28273         this.id = this.el.id || Roo.id();
28274         this.el.on('submit', this.onSubmit, this);
28275         this.el.addClass('x-form');
28276     },
28277
28278     // private
28279     onSubmit : function(e){
28280         e.stopEvent();
28281     },
28282
28283     /**
28284      * Returns true if client-side validation on the form is successful.
28285      * @return Boolean
28286      */
28287     isValid : function(){
28288         var valid = true;
28289         this.items.each(function(f){
28290            if(!f.validate()){
28291                valid = false;
28292            }
28293         });
28294         return valid;
28295     },
28296
28297     /**
28298      * Returns true if any fields in this form have changed since their original load.
28299      * @return Boolean
28300      */
28301     isDirty : function(){
28302         var dirty = false;
28303         this.items.each(function(f){
28304            if(f.isDirty()){
28305                dirty = true;
28306                return false;
28307            }
28308         });
28309         return dirty;
28310     },
28311
28312     /**
28313      * Performs a predefined action (submit or load) or custom actions you define on this form.
28314      * @param {String} actionName The name of the action type
28315      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
28316      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
28317      * accept other config options):
28318      * <pre>
28319 Property          Type             Description
28320 ----------------  ---------------  ----------------------------------------------------------------------------------
28321 url               String           The url for the action (defaults to the form's url)
28322 method            String           The form method to use (defaults to the form's method, or POST if not defined)
28323 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
28324 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
28325                                    validate the form on the client (defaults to false)
28326      * </pre>
28327      * @return {BasicForm} this
28328      */
28329     doAction : function(action, options){
28330         if(typeof action == 'string'){
28331             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
28332         }
28333         if(this.fireEvent('beforeaction', this, action) !== false){
28334             this.beforeAction(action);
28335             action.run.defer(100, action);
28336         }
28337         return this;
28338     },
28339
28340     /**
28341      * Shortcut to do a submit action.
28342      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28343      * @return {BasicForm} this
28344      */
28345     submit : function(options){
28346         this.doAction('submit', options);
28347         return this;
28348     },
28349
28350     /**
28351      * Shortcut to do a load action.
28352      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28353      * @return {BasicForm} this
28354      */
28355     load : function(options){
28356         this.doAction('load', options);
28357         return this;
28358     },
28359
28360     /**
28361      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
28362      * @param {Record} record The record to edit
28363      * @return {BasicForm} this
28364      */
28365     updateRecord : function(record){
28366         record.beginEdit();
28367         var fs = record.fields;
28368         fs.each(function(f){
28369             var field = this.findField(f.name);
28370             if(field){
28371                 record.set(f.name, field.getValue());
28372             }
28373         }, this);
28374         record.endEdit();
28375         return this;
28376     },
28377
28378     /**
28379      * Loads an Roo.data.Record into this form.
28380      * @param {Record} record The record to load
28381      * @return {BasicForm} this
28382      */
28383     loadRecord : function(record){
28384         this.setValues(record.data);
28385         return this;
28386     },
28387
28388     // private
28389     beforeAction : function(action){
28390         var o = action.options;
28391         
28392        
28393         if(this.waitMsgTarget === true){
28394             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
28395         }else if(this.waitMsgTarget){
28396             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
28397             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
28398         }else {
28399             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
28400         }
28401          
28402     },
28403
28404     // private
28405     afterAction : function(action, success){
28406         this.activeAction = null;
28407         var o = action.options;
28408         
28409         if(this.waitMsgTarget === true){
28410             this.el.unmask();
28411         }else if(this.waitMsgTarget){
28412             this.waitMsgTarget.unmask();
28413         }else{
28414             Roo.MessageBox.updateProgress(1);
28415             Roo.MessageBox.hide();
28416         }
28417          
28418         if(success){
28419             if(o.reset){
28420                 this.reset();
28421             }
28422             Roo.callback(o.success, o.scope, [this, action]);
28423             this.fireEvent('actioncomplete', this, action);
28424             
28425         }else{
28426             
28427             // failure condition..
28428             // we have a scenario where updates need confirming.
28429             // eg. if a locking scenario exists..
28430             // we look for { errors : { needs_confirm : true }} in the response.
28431             if (
28432                 (typeof(action.result) != 'undefined')  &&
28433                 (typeof(action.result.errors) != 'undefined')  &&
28434                 (typeof(action.result.errors.needs_confirm) != 'undefined')
28435            ){
28436                 var _t = this;
28437                 Roo.MessageBox.confirm(
28438                     "Change requires confirmation",
28439                     action.result.errorMsg,
28440                     function(r) {
28441                         if (r != 'yes') {
28442                             return;
28443                         }
28444                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
28445                     }
28446                     
28447                 );
28448                 
28449                 
28450                 
28451                 return;
28452             }
28453             
28454             Roo.callback(o.failure, o.scope, [this, action]);
28455             // show an error message if no failed handler is set..
28456             if (!this.hasListener('actionfailed')) {
28457                 Roo.MessageBox.alert("Error",
28458                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
28459                         action.result.errorMsg :
28460                         "Saving Failed, please check your entries or try again"
28461                 );
28462             }
28463             
28464             this.fireEvent('actionfailed', this, action);
28465         }
28466         
28467     },
28468
28469     /**
28470      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
28471      * @param {String} id The value to search for
28472      * @return Field
28473      */
28474     findField : function(id){
28475         var field = this.items.get(id);
28476         if(!field){
28477             this.items.each(function(f){
28478                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
28479                     field = f;
28480                     return false;
28481                 }
28482             });
28483         }
28484         return field || null;
28485     },
28486
28487     /**
28488      * Add a secondary form to this one, 
28489      * Used to provide tabbed forms. One form is primary, with hidden values 
28490      * which mirror the elements from the other forms.
28491      * 
28492      * @param {Roo.form.Form} form to add.
28493      * 
28494      */
28495     addForm : function(form)
28496     {
28497        
28498         if (this.childForms.indexOf(form) > -1) {
28499             // already added..
28500             return;
28501         }
28502         this.childForms.push(form);
28503         var n = '';
28504         Roo.each(form.allItems, function (fe) {
28505             
28506             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
28507             if (this.findField(n)) { // already added..
28508                 return;
28509             }
28510             var add = new Roo.form.Hidden({
28511                 name : n
28512             });
28513             add.render(this.el);
28514             
28515             this.add( add );
28516         }, this);
28517         
28518     },
28519     /**
28520      * Mark fields in this form invalid in bulk.
28521      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
28522      * @return {BasicForm} this
28523      */
28524     markInvalid : function(errors){
28525         if(errors instanceof Array){
28526             for(var i = 0, len = errors.length; i < len; i++){
28527                 var fieldError = errors[i];
28528                 var f = this.findField(fieldError.id);
28529                 if(f){
28530                     f.markInvalid(fieldError.msg);
28531                 }
28532             }
28533         }else{
28534             var field, id;
28535             for(id in errors){
28536                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
28537                     field.markInvalid(errors[id]);
28538                 }
28539             }
28540         }
28541         Roo.each(this.childForms || [], function (f) {
28542             f.markInvalid(errors);
28543         });
28544         
28545         return this;
28546     },
28547
28548     /**
28549      * Set values for fields in this form in bulk.
28550      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
28551      * @return {BasicForm} this
28552      */
28553     setValues : function(values){
28554         if(values instanceof Array){ // array of objects
28555             for(var i = 0, len = values.length; i < len; i++){
28556                 var v = values[i];
28557                 var f = this.findField(v.id);
28558                 if(f){
28559                     f.setValue(v.value);
28560                     if(this.trackResetOnLoad){
28561                         f.originalValue = f.getValue();
28562                     }
28563                 }
28564             }
28565         }else{ // object hash
28566             var field, id;
28567             for(id in values){
28568                 if(typeof values[id] != 'function' && (field = this.findField(id))){
28569                     
28570                     if (field.setFromData && 
28571                         field.valueField && 
28572                         field.displayField &&
28573                         // combos' with local stores can 
28574                         // be queried via setValue()
28575                         // to set their value..
28576                         (field.store && !field.store.isLocal)
28577                         ) {
28578                         // it's a combo
28579                         var sd = { };
28580                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
28581                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
28582                         field.setFromData(sd);
28583                         
28584                     } else {
28585                         field.setValue(values[id]);
28586                     }
28587                     
28588                     
28589                     if(this.trackResetOnLoad){
28590                         field.originalValue = field.getValue();
28591                     }
28592                 }
28593             }
28594         }
28595          
28596         Roo.each(this.childForms || [], function (f) {
28597             f.setValues(values);
28598         });
28599                 
28600         return this;
28601     },
28602
28603     /**
28604      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
28605      * they are returned as an array.
28606      * @param {Boolean} asString
28607      * @return {Object}
28608      */
28609     getValues : function(asString){
28610         if (this.childForms) {
28611             // copy values from the child forms
28612             Roo.each(this.childForms, function (f) {
28613                 this.setValues(f.getValues());
28614             }, this);
28615         }
28616         
28617         
28618         
28619         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
28620         if(asString === true){
28621             return fs;
28622         }
28623         return Roo.urlDecode(fs);
28624     },
28625     
28626     /**
28627      * Returns the fields in this form as an object with key/value pairs. 
28628      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
28629      * @return {Object}
28630      */
28631     getFieldValues : function(with_hidden)
28632     {
28633         if (this.childForms) {
28634             // copy values from the child forms
28635             // should this call getFieldValues - probably not as we do not currently copy
28636             // hidden fields when we generate..
28637             Roo.each(this.childForms, function (f) {
28638                 this.setValues(f.getValues());
28639             }, this);
28640         }
28641         
28642         var ret = {};
28643         this.items.each(function(f){
28644             if (!f.getName()) {
28645                 return;
28646             }
28647             var v = f.getValue();
28648             if (f.inputType =='radio') {
28649                 if (typeof(ret[f.getName()]) == 'undefined') {
28650                     ret[f.getName()] = ''; // empty..
28651                 }
28652                 
28653                 if (!f.el.dom.checked) {
28654                     return;
28655                     
28656                 }
28657                 v = f.el.dom.value;
28658                 
28659             }
28660             
28661             // not sure if this supported any more..
28662             if ((typeof(v) == 'object') && f.getRawValue) {
28663                 v = f.getRawValue() ; // dates..
28664             }
28665             // combo boxes where name != hiddenName...
28666             if (f.name != f.getName()) {
28667                 ret[f.name] = f.getRawValue();
28668             }
28669             ret[f.getName()] = v;
28670         });
28671         
28672         return ret;
28673     },
28674
28675     /**
28676      * Clears all invalid messages in this form.
28677      * @return {BasicForm} this
28678      */
28679     clearInvalid : function(){
28680         this.items.each(function(f){
28681            f.clearInvalid();
28682         });
28683         
28684         Roo.each(this.childForms || [], function (f) {
28685             f.clearInvalid();
28686         });
28687         
28688         
28689         return this;
28690     },
28691
28692     /**
28693      * Resets this form.
28694      * @return {BasicForm} this
28695      */
28696     reset : function(){
28697         this.items.each(function(f){
28698             f.reset();
28699         });
28700         
28701         Roo.each(this.childForms || [], function (f) {
28702             f.reset();
28703         });
28704        
28705         
28706         return this;
28707     },
28708
28709     /**
28710      * Add Roo.form components to this form.
28711      * @param {Field} field1
28712      * @param {Field} field2 (optional)
28713      * @param {Field} etc (optional)
28714      * @return {BasicForm} this
28715      */
28716     add : function(){
28717         this.items.addAll(Array.prototype.slice.call(arguments, 0));
28718         return this;
28719     },
28720
28721
28722     /**
28723      * Removes a field from the items collection (does NOT remove its markup).
28724      * @param {Field} field
28725      * @return {BasicForm} this
28726      */
28727     remove : function(field){
28728         this.items.remove(field);
28729         return this;
28730     },
28731
28732     /**
28733      * Looks at the fields in this form, checks them for an id attribute,
28734      * and calls applyTo on the existing dom element with that id.
28735      * @return {BasicForm} this
28736      */
28737     render : function(){
28738         this.items.each(function(f){
28739             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
28740                 f.applyTo(f.id);
28741             }
28742         });
28743         return this;
28744     },
28745
28746     /**
28747      * Calls {@link Ext#apply} for all fields in this form with the passed object.
28748      * @param {Object} values
28749      * @return {BasicForm} this
28750      */
28751     applyToFields : function(o){
28752         this.items.each(function(f){
28753            Roo.apply(f, o);
28754         });
28755         return this;
28756     },
28757
28758     /**
28759      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
28760      * @param {Object} values
28761      * @return {BasicForm} this
28762      */
28763     applyIfToFields : function(o){
28764         this.items.each(function(f){
28765            Roo.applyIf(f, o);
28766         });
28767         return this;
28768     }
28769 });
28770
28771 // back compat
28772 Roo.BasicForm = Roo.form.BasicForm;/*
28773  * Based on:
28774  * Ext JS Library 1.1.1
28775  * Copyright(c) 2006-2007, Ext JS, LLC.
28776  *
28777  * Originally Released Under LGPL - original licence link has changed is not relivant.
28778  *
28779  * Fork - LGPL
28780  * <script type="text/javascript">
28781  */
28782
28783 /**
28784  * @class Roo.form.Form
28785  * @extends Roo.form.BasicForm
28786  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
28787  * @constructor
28788  * @param {Object} config Configuration options
28789  */
28790 Roo.form.Form = function(config){
28791     var xitems =  [];
28792     if (config.items) {
28793         xitems = config.items;
28794         delete config.items;
28795     }
28796    
28797     
28798     Roo.form.Form.superclass.constructor.call(this, null, config);
28799     this.url = this.url || this.action;
28800     if(!this.root){
28801         this.root = new Roo.form.Layout(Roo.applyIf({
28802             id: Roo.id()
28803         }, config));
28804     }
28805     this.active = this.root;
28806     /**
28807      * Array of all the buttons that have been added to this form via {@link addButton}
28808      * @type Array
28809      */
28810     this.buttons = [];
28811     this.allItems = [];
28812     this.addEvents({
28813         /**
28814          * @event clientvalidation
28815          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
28816          * @param {Form} this
28817          * @param {Boolean} valid true if the form has passed client-side validation
28818          */
28819         clientvalidation: true,
28820         /**
28821          * @event rendered
28822          * Fires when the form is rendered
28823          * @param {Roo.form.Form} form
28824          */
28825         rendered : true
28826     });
28827     
28828     if (this.progressUrl) {
28829             // push a hidden field onto the list of fields..
28830             this.addxtype( {
28831                     xns: Roo.form, 
28832                     xtype : 'Hidden', 
28833                     name : 'UPLOAD_IDENTIFIER' 
28834             });
28835         }
28836         
28837     
28838     Roo.each(xitems, this.addxtype, this);
28839     
28840     
28841     
28842 };
28843
28844 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
28845     /**
28846      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
28847      */
28848     /**
28849      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
28850      */
28851     /**
28852      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
28853      */
28854     buttonAlign:'center',
28855
28856     /**
28857      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
28858      */
28859     minButtonWidth:75,
28860
28861     /**
28862      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
28863      * This property cascades to child containers if not set.
28864      */
28865     labelAlign:'left',
28866
28867     /**
28868      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
28869      * fires a looping event with that state. This is required to bind buttons to the valid
28870      * state using the config value formBind:true on the button.
28871      */
28872     monitorValid : false,
28873
28874     /**
28875      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
28876      */
28877     monitorPoll : 200,
28878     
28879     /**
28880      * @cfg {String} progressUrl - Url to return progress data 
28881      */
28882     
28883     progressUrl : false,
28884   
28885     /**
28886      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
28887      * fields are added and the column is closed. If no fields are passed the column remains open
28888      * until end() is called.
28889      * @param {Object} config The config to pass to the column
28890      * @param {Field} field1 (optional)
28891      * @param {Field} field2 (optional)
28892      * @param {Field} etc (optional)
28893      * @return Column The column container object
28894      */
28895     column : function(c){
28896         var col = new Roo.form.Column(c);
28897         this.start(col);
28898         if(arguments.length > 1){ // duplicate code required because of Opera
28899             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28900             this.end();
28901         }
28902         return col;
28903     },
28904
28905     /**
28906      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
28907      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
28908      * until end() is called.
28909      * @param {Object} config The config to pass to the fieldset
28910      * @param {Field} field1 (optional)
28911      * @param {Field} field2 (optional)
28912      * @param {Field} etc (optional)
28913      * @return FieldSet The fieldset container object
28914      */
28915     fieldset : function(c){
28916         var fs = new Roo.form.FieldSet(c);
28917         this.start(fs);
28918         if(arguments.length > 1){ // duplicate code required because of Opera
28919             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28920             this.end();
28921         }
28922         return fs;
28923     },
28924
28925     /**
28926      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
28927      * fields are added and the container is closed. If no fields are passed the container remains open
28928      * until end() is called.
28929      * @param {Object} config The config to pass to the Layout
28930      * @param {Field} field1 (optional)
28931      * @param {Field} field2 (optional)
28932      * @param {Field} etc (optional)
28933      * @return Layout The container object
28934      */
28935     container : function(c){
28936         var l = new Roo.form.Layout(c);
28937         this.start(l);
28938         if(arguments.length > 1){ // duplicate code required because of Opera
28939             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28940             this.end();
28941         }
28942         return l;
28943     },
28944
28945     /**
28946      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
28947      * @param {Object} container A Roo.form.Layout or subclass of Layout
28948      * @return {Form} this
28949      */
28950     start : function(c){
28951         // cascade label info
28952         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
28953         this.active.stack.push(c);
28954         c.ownerCt = this.active;
28955         this.active = c;
28956         return this;
28957     },
28958
28959     /**
28960      * Closes the current open container
28961      * @return {Form} this
28962      */
28963     end : function(){
28964         if(this.active == this.root){
28965             return this;
28966         }
28967         this.active = this.active.ownerCt;
28968         return this;
28969     },
28970
28971     /**
28972      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
28973      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
28974      * as the label of the field.
28975      * @param {Field} field1
28976      * @param {Field} field2 (optional)
28977      * @param {Field} etc. (optional)
28978      * @return {Form} this
28979      */
28980     add : function(){
28981         this.active.stack.push.apply(this.active.stack, arguments);
28982         this.allItems.push.apply(this.allItems,arguments);
28983         var r = [];
28984         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
28985             if(a[i].isFormField){
28986                 r.push(a[i]);
28987             }
28988         }
28989         if(r.length > 0){
28990             Roo.form.Form.superclass.add.apply(this, r);
28991         }
28992         return this;
28993     },
28994     
28995
28996     
28997     
28998     
28999      /**
29000      * Find any element that has been added to a form, using it's ID or name
29001      * This can include framesets, columns etc. along with regular fields..
29002      * @param {String} id - id or name to find.
29003      
29004      * @return {Element} e - or false if nothing found.
29005      */
29006     findbyId : function(id)
29007     {
29008         var ret = false;
29009         if (!id) {
29010             return ret;
29011         }
29012         Roo.each(this.allItems, function(f){
29013             if (f.id == id || f.name == id ){
29014                 ret = f;
29015                 return false;
29016             }
29017         });
29018         return ret;
29019     },
29020
29021     
29022     
29023     /**
29024      * Render this form into the passed container. This should only be called once!
29025      * @param {String/HTMLElement/Element} container The element this component should be rendered into
29026      * @return {Form} this
29027      */
29028     render : function(ct)
29029     {
29030         
29031         
29032         
29033         ct = Roo.get(ct);
29034         var o = this.autoCreate || {
29035             tag: 'form',
29036             method : this.method || 'POST',
29037             id : this.id || Roo.id()
29038         };
29039         this.initEl(ct.createChild(o));
29040
29041         this.root.render(this.el);
29042         
29043        
29044              
29045         this.items.each(function(f){
29046             f.render('x-form-el-'+f.id);
29047         });
29048
29049         if(this.buttons.length > 0){
29050             // tables are required to maintain order and for correct IE layout
29051             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
29052                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
29053                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29054             }}, null, true);
29055             var tr = tb.getElementsByTagName('tr')[0];
29056             for(var i = 0, len = this.buttons.length; i < len; i++) {
29057                 var b = this.buttons[i];
29058                 var td = document.createElement('td');
29059                 td.className = 'x-form-btn-td';
29060                 b.render(tr.appendChild(td));
29061             }
29062         }
29063         if(this.monitorValid){ // initialize after render
29064             this.startMonitoring();
29065         }
29066         this.fireEvent('rendered', this);
29067         return this;
29068     },
29069
29070     /**
29071      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
29072      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29073      * object or a valid Roo.DomHelper element config
29074      * @param {Function} handler The function called when the button is clicked
29075      * @param {Object} scope (optional) The scope of the handler function
29076      * @return {Roo.Button}
29077      */
29078     addButton : function(config, handler, scope){
29079         var bc = {
29080             handler: handler,
29081             scope: scope,
29082             minWidth: this.minButtonWidth,
29083             hideParent:true
29084         };
29085         if(typeof config == "string"){
29086             bc.text = config;
29087         }else{
29088             Roo.apply(bc, config);
29089         }
29090         var btn = new Roo.Button(null, bc);
29091         this.buttons.push(btn);
29092         return btn;
29093     },
29094
29095      /**
29096      * Adds a series of form elements (using the xtype property as the factory method.
29097      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
29098      * @param {Object} config 
29099      */
29100     
29101     addxtype : function()
29102     {
29103         var ar = Array.prototype.slice.call(arguments, 0);
29104         var ret = false;
29105         for(var i = 0; i < ar.length; i++) {
29106             if (!ar[i]) {
29107                 continue; // skip -- if this happends something invalid got sent, we 
29108                 // should ignore it, as basically that interface element will not show up
29109                 // and that should be pretty obvious!!
29110             }
29111             
29112             if (Roo.form[ar[i].xtype]) {
29113                 ar[i].form = this;
29114                 var fe = Roo.factory(ar[i], Roo.form);
29115                 if (!ret) {
29116                     ret = fe;
29117                 }
29118                 fe.form = this;
29119                 if (fe.store) {
29120                     fe.store.form = this;
29121                 }
29122                 if (fe.isLayout) {  
29123                          
29124                     this.start(fe);
29125                     this.allItems.push(fe);
29126                     if (fe.items && fe.addxtype) {
29127                         fe.addxtype.apply(fe, fe.items);
29128                         delete fe.items;
29129                     }
29130                      this.end();
29131                     continue;
29132                 }
29133                 
29134                 
29135                  
29136                 this.add(fe);
29137               //  console.log('adding ' + ar[i].xtype);
29138             }
29139             if (ar[i].xtype == 'Button') {  
29140                 //console.log('adding button');
29141                 //console.log(ar[i]);
29142                 this.addButton(ar[i]);
29143                 this.allItems.push(fe);
29144                 continue;
29145             }
29146             
29147             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
29148                 alert('end is not supported on xtype any more, use items');
29149             //    this.end();
29150             //    //console.log('adding end');
29151             }
29152             
29153         }
29154         return ret;
29155     },
29156     
29157     /**
29158      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
29159      * option "monitorValid"
29160      */
29161     startMonitoring : function(){
29162         if(!this.bound){
29163             this.bound = true;
29164             Roo.TaskMgr.start({
29165                 run : this.bindHandler,
29166                 interval : this.monitorPoll || 200,
29167                 scope: this
29168             });
29169         }
29170     },
29171
29172     /**
29173      * Stops monitoring of the valid state of this form
29174      */
29175     stopMonitoring : function(){
29176         this.bound = false;
29177     },
29178
29179     // private
29180     bindHandler : function(){
29181         if(!this.bound){
29182             return false; // stops binding
29183         }
29184         var valid = true;
29185         this.items.each(function(f){
29186             if(!f.isValid(true)){
29187                 valid = false;
29188                 return false;
29189             }
29190         });
29191         for(var i = 0, len = this.buttons.length; i < len; i++){
29192             var btn = this.buttons[i];
29193             if(btn.formBind === true && btn.disabled === valid){
29194                 btn.setDisabled(!valid);
29195             }
29196         }
29197         this.fireEvent('clientvalidation', this, valid);
29198     }
29199     
29200     
29201     
29202     
29203     
29204     
29205     
29206     
29207 });
29208
29209
29210 // back compat
29211 Roo.Form = Roo.form.Form;
29212 /*
29213  * Based on:
29214  * Ext JS Library 1.1.1
29215  * Copyright(c) 2006-2007, Ext JS, LLC.
29216  *
29217  * Originally Released Under LGPL - original licence link has changed is not relivant.
29218  *
29219  * Fork - LGPL
29220  * <script type="text/javascript">
29221  */
29222
29223 // as we use this in bootstrap.
29224 Roo.namespace('Roo.form');
29225  /**
29226  * @class Roo.form.Action
29227  * Internal Class used to handle form actions
29228  * @constructor
29229  * @param {Roo.form.BasicForm} el The form element or its id
29230  * @param {Object} config Configuration options
29231  */
29232
29233  
29234  
29235 // define the action interface
29236 Roo.form.Action = function(form, options){
29237     this.form = form;
29238     this.options = options || {};
29239 };
29240 /**
29241  * Client Validation Failed
29242  * @const 
29243  */
29244 Roo.form.Action.CLIENT_INVALID = 'client';
29245 /**
29246  * Server Validation Failed
29247  * @const 
29248  */
29249 Roo.form.Action.SERVER_INVALID = 'server';
29250  /**
29251  * Connect to Server Failed
29252  * @const 
29253  */
29254 Roo.form.Action.CONNECT_FAILURE = 'connect';
29255 /**
29256  * Reading Data from Server Failed
29257  * @const 
29258  */
29259 Roo.form.Action.LOAD_FAILURE = 'load';
29260
29261 Roo.form.Action.prototype = {
29262     type : 'default',
29263     failureType : undefined,
29264     response : undefined,
29265     result : undefined,
29266
29267     // interface method
29268     run : function(options){
29269
29270     },
29271
29272     // interface method
29273     success : function(response){
29274
29275     },
29276
29277     // interface method
29278     handleResponse : function(response){
29279
29280     },
29281
29282     // default connection failure
29283     failure : function(response){
29284         
29285         this.response = response;
29286         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29287         this.form.afterAction(this, false);
29288     },
29289
29290     processResponse : function(response){
29291         this.response = response;
29292         if(!response.responseText){
29293             return true;
29294         }
29295         this.result = this.handleResponse(response);
29296         return this.result;
29297     },
29298
29299     // utility functions used internally
29300     getUrl : function(appendParams){
29301         var url = this.options.url || this.form.url || this.form.el.dom.action;
29302         if(appendParams){
29303             var p = this.getParams();
29304             if(p){
29305                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
29306             }
29307         }
29308         return url;
29309     },
29310
29311     getMethod : function(){
29312         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
29313     },
29314
29315     getParams : function(){
29316         var bp = this.form.baseParams;
29317         var p = this.options.params;
29318         if(p){
29319             if(typeof p == "object"){
29320                 p = Roo.urlEncode(Roo.applyIf(p, bp));
29321             }else if(typeof p == 'string' && bp){
29322                 p += '&' + Roo.urlEncode(bp);
29323             }
29324         }else if(bp){
29325             p = Roo.urlEncode(bp);
29326         }
29327         return p;
29328     },
29329
29330     createCallback : function(){
29331         return {
29332             success: this.success,
29333             failure: this.failure,
29334             scope: this,
29335             timeout: (this.form.timeout*1000),
29336             upload: this.form.fileUpload ? this.success : undefined
29337         };
29338     }
29339 };
29340
29341 Roo.form.Action.Submit = function(form, options){
29342     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
29343 };
29344
29345 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
29346     type : 'submit',
29347
29348     haveProgress : false,
29349     uploadComplete : false,
29350     
29351     // uploadProgress indicator.
29352     uploadProgress : function()
29353     {
29354         if (!this.form.progressUrl) {
29355             return;
29356         }
29357         
29358         if (!this.haveProgress) {
29359             Roo.MessageBox.progress("Uploading", "Uploading");
29360         }
29361         if (this.uploadComplete) {
29362            Roo.MessageBox.hide();
29363            return;
29364         }
29365         
29366         this.haveProgress = true;
29367    
29368         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
29369         
29370         var c = new Roo.data.Connection();
29371         c.request({
29372             url : this.form.progressUrl,
29373             params: {
29374                 id : uid
29375             },
29376             method: 'GET',
29377             success : function(req){
29378                //console.log(data);
29379                 var rdata = false;
29380                 var edata;
29381                 try  {
29382                    rdata = Roo.decode(req.responseText)
29383                 } catch (e) {
29384                     Roo.log("Invalid data from server..");
29385                     Roo.log(edata);
29386                     return;
29387                 }
29388                 if (!rdata || !rdata.success) {
29389                     Roo.log(rdata);
29390                     Roo.MessageBox.alert(Roo.encode(rdata));
29391                     return;
29392                 }
29393                 var data = rdata.data;
29394                 
29395                 if (this.uploadComplete) {
29396                    Roo.MessageBox.hide();
29397                    return;
29398                 }
29399                    
29400                 if (data){
29401                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
29402                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
29403                     );
29404                 }
29405                 this.uploadProgress.defer(2000,this);
29406             },
29407        
29408             failure: function(data) {
29409                 Roo.log('progress url failed ');
29410                 Roo.log(data);
29411             },
29412             scope : this
29413         });
29414            
29415     },
29416     
29417     
29418     run : function()
29419     {
29420         // run get Values on the form, so it syncs any secondary forms.
29421         this.form.getValues();
29422         
29423         var o = this.options;
29424         var method = this.getMethod();
29425         var isPost = method == 'POST';
29426         if(o.clientValidation === false || this.form.isValid()){
29427             
29428             if (this.form.progressUrl) {
29429                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
29430                     (new Date() * 1) + '' + Math.random());
29431                     
29432             } 
29433             
29434             
29435             Roo.Ajax.request(Roo.apply(this.createCallback(), {
29436                 form:this.form.el.dom,
29437                 url:this.getUrl(!isPost),
29438                 method: method,
29439                 params:isPost ? this.getParams() : null,
29440                 isUpload: this.form.fileUpload
29441             }));
29442             
29443             this.uploadProgress();
29444
29445         }else if (o.clientValidation !== false){ // client validation failed
29446             this.failureType = Roo.form.Action.CLIENT_INVALID;
29447             this.form.afterAction(this, false);
29448         }
29449     },
29450
29451     success : function(response)
29452     {
29453         this.uploadComplete= true;
29454         if (this.haveProgress) {
29455             Roo.MessageBox.hide();
29456         }
29457         
29458         
29459         var result = this.processResponse(response);
29460         if(result === true || result.success){
29461             this.form.afterAction(this, true);
29462             return;
29463         }
29464         if(result.errors){
29465             this.form.markInvalid(result.errors);
29466             this.failureType = Roo.form.Action.SERVER_INVALID;
29467         }
29468         this.form.afterAction(this, false);
29469     },
29470     failure : function(response)
29471     {
29472         this.uploadComplete= true;
29473         if (this.haveProgress) {
29474             Roo.MessageBox.hide();
29475         }
29476         
29477         this.response = response;
29478         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29479         this.form.afterAction(this, false);
29480     },
29481     
29482     handleResponse : function(response){
29483         if(this.form.errorReader){
29484             var rs = this.form.errorReader.read(response);
29485             var errors = [];
29486             if(rs.records){
29487                 for(var i = 0, len = rs.records.length; i < len; i++) {
29488                     var r = rs.records[i];
29489                     errors[i] = r.data;
29490                 }
29491             }
29492             if(errors.length < 1){
29493                 errors = null;
29494             }
29495             return {
29496                 success : rs.success,
29497                 errors : errors
29498             };
29499         }
29500         var ret = false;
29501         try {
29502             ret = Roo.decode(response.responseText);
29503         } catch (e) {
29504             ret = {
29505                 success: false,
29506                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
29507                 errors : []
29508             };
29509         }
29510         return ret;
29511         
29512     }
29513 });
29514
29515
29516 Roo.form.Action.Load = function(form, options){
29517     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
29518     this.reader = this.form.reader;
29519 };
29520
29521 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
29522     type : 'load',
29523
29524     run : function(){
29525         
29526         Roo.Ajax.request(Roo.apply(
29527                 this.createCallback(), {
29528                     method:this.getMethod(),
29529                     url:this.getUrl(false),
29530                     params:this.getParams()
29531         }));
29532     },
29533
29534     success : function(response){
29535         
29536         var result = this.processResponse(response);
29537         if(result === true || !result.success || !result.data){
29538             this.failureType = Roo.form.Action.LOAD_FAILURE;
29539             this.form.afterAction(this, false);
29540             return;
29541         }
29542         this.form.clearInvalid();
29543         this.form.setValues(result.data);
29544         this.form.afterAction(this, true);
29545     },
29546
29547     handleResponse : function(response){
29548         if(this.form.reader){
29549             var rs = this.form.reader.read(response);
29550             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
29551             return {
29552                 success : rs.success,
29553                 data : data
29554             };
29555         }
29556         return Roo.decode(response.responseText);
29557     }
29558 });
29559
29560 Roo.form.Action.ACTION_TYPES = {
29561     'load' : Roo.form.Action.Load,
29562     'submit' : Roo.form.Action.Submit
29563 };/*
29564  * Based on:
29565  * Ext JS Library 1.1.1
29566  * Copyright(c) 2006-2007, Ext JS, LLC.
29567  *
29568  * Originally Released Under LGPL - original licence link has changed is not relivant.
29569  *
29570  * Fork - LGPL
29571  * <script type="text/javascript">
29572  */
29573  
29574 /**
29575  * @class Roo.form.Layout
29576  * @extends Roo.Component
29577  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
29578  * @constructor
29579  * @param {Object} config Configuration options
29580  */
29581 Roo.form.Layout = function(config){
29582     var xitems = [];
29583     if (config.items) {
29584         xitems = config.items;
29585         delete config.items;
29586     }
29587     Roo.form.Layout.superclass.constructor.call(this, config);
29588     this.stack = [];
29589     Roo.each(xitems, this.addxtype, this);
29590      
29591 };
29592
29593 Roo.extend(Roo.form.Layout, Roo.Component, {
29594     /**
29595      * @cfg {String/Object} autoCreate
29596      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
29597      */
29598     /**
29599      * @cfg {String/Object/Function} style
29600      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
29601      * a function which returns such a specification.
29602      */
29603     /**
29604      * @cfg {String} labelAlign
29605      * Valid values are "left," "top" and "right" (defaults to "left")
29606      */
29607     /**
29608      * @cfg {Number} labelWidth
29609      * Fixed width in pixels of all field labels (defaults to undefined)
29610      */
29611     /**
29612      * @cfg {Boolean} clear
29613      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
29614      */
29615     clear : true,
29616     /**
29617      * @cfg {String} labelSeparator
29618      * The separator to use after field labels (defaults to ':')
29619      */
29620     labelSeparator : ':',
29621     /**
29622      * @cfg {Boolean} hideLabels
29623      * True to suppress the display of field labels in this layout (defaults to false)
29624      */
29625     hideLabels : false,
29626
29627     // private
29628     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
29629     
29630     isLayout : true,
29631     
29632     // private
29633     onRender : function(ct, position){
29634         if(this.el){ // from markup
29635             this.el = Roo.get(this.el);
29636         }else {  // generate
29637             var cfg = this.getAutoCreate();
29638             this.el = ct.createChild(cfg, position);
29639         }
29640         if(this.style){
29641             this.el.applyStyles(this.style);
29642         }
29643         if(this.labelAlign){
29644             this.el.addClass('x-form-label-'+this.labelAlign);
29645         }
29646         if(this.hideLabels){
29647             this.labelStyle = "display:none";
29648             this.elementStyle = "padding-left:0;";
29649         }else{
29650             if(typeof this.labelWidth == 'number'){
29651                 this.labelStyle = "width:"+this.labelWidth+"px;";
29652                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
29653             }
29654             if(this.labelAlign == 'top'){
29655                 this.labelStyle = "width:auto;";
29656                 this.elementStyle = "padding-left:0;";
29657             }
29658         }
29659         var stack = this.stack;
29660         var slen = stack.length;
29661         if(slen > 0){
29662             if(!this.fieldTpl){
29663                 var t = new Roo.Template(
29664                     '<div class="x-form-item {5}">',
29665                         '<label for="{0}" style="{2}">{1}{4}</label>',
29666                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29667                         '</div>',
29668                     '</div><div class="x-form-clear-left"></div>'
29669                 );
29670                 t.disableFormats = true;
29671                 t.compile();
29672                 Roo.form.Layout.prototype.fieldTpl = t;
29673             }
29674             for(var i = 0; i < slen; i++) {
29675                 if(stack[i].isFormField){
29676                     this.renderField(stack[i]);
29677                 }else{
29678                     this.renderComponent(stack[i]);
29679                 }
29680             }
29681         }
29682         if(this.clear){
29683             this.el.createChild({cls:'x-form-clear'});
29684         }
29685     },
29686
29687     // private
29688     renderField : function(f){
29689         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
29690                f.id, //0
29691                f.fieldLabel, //1
29692                f.labelStyle||this.labelStyle||'', //2
29693                this.elementStyle||'', //3
29694                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
29695                f.itemCls||this.itemCls||''  //5
29696        ], true).getPrevSibling());
29697     },
29698
29699     // private
29700     renderComponent : function(c){
29701         c.render(c.isLayout ? this.el : this.el.createChild());    
29702     },
29703     /**
29704      * Adds a object form elements (using the xtype property as the factory method.)
29705      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
29706      * @param {Object} config 
29707      */
29708     addxtype : function(o)
29709     {
29710         // create the lement.
29711         o.form = this.form;
29712         var fe = Roo.factory(o, Roo.form);
29713         this.form.allItems.push(fe);
29714         this.stack.push(fe);
29715         
29716         if (fe.isFormField) {
29717             this.form.items.add(fe);
29718         }
29719          
29720         return fe;
29721     }
29722 });
29723
29724 /**
29725  * @class Roo.form.Column
29726  * @extends Roo.form.Layout
29727  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
29728  * @constructor
29729  * @param {Object} config Configuration options
29730  */
29731 Roo.form.Column = function(config){
29732     Roo.form.Column.superclass.constructor.call(this, config);
29733 };
29734
29735 Roo.extend(Roo.form.Column, Roo.form.Layout, {
29736     /**
29737      * @cfg {Number/String} width
29738      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29739      */
29740     /**
29741      * @cfg {String/Object} autoCreate
29742      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
29743      */
29744
29745     // private
29746     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
29747
29748     // private
29749     onRender : function(ct, position){
29750         Roo.form.Column.superclass.onRender.call(this, ct, position);
29751         if(this.width){
29752             this.el.setWidth(this.width);
29753         }
29754     }
29755 });
29756
29757
29758 /**
29759  * @class Roo.form.Row
29760  * @extends Roo.form.Layout
29761  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
29762  * @constructor
29763  * @param {Object} config Configuration options
29764  */
29765
29766  
29767 Roo.form.Row = function(config){
29768     Roo.form.Row.superclass.constructor.call(this, config);
29769 };
29770  
29771 Roo.extend(Roo.form.Row, Roo.form.Layout, {
29772       /**
29773      * @cfg {Number/String} width
29774      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29775      */
29776     /**
29777      * @cfg {Number/String} height
29778      * The fixed height of the column in pixels or CSS value (defaults to "auto")
29779      */
29780     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
29781     
29782     padWidth : 20,
29783     // private
29784     onRender : function(ct, position){
29785         //console.log('row render');
29786         if(!this.rowTpl){
29787             var t = new Roo.Template(
29788                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
29789                     '<label for="{0}" style="{2}">{1}{4}</label>',
29790                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29791                     '</div>',
29792                 '</div>'
29793             );
29794             t.disableFormats = true;
29795             t.compile();
29796             Roo.form.Layout.prototype.rowTpl = t;
29797         }
29798         this.fieldTpl = this.rowTpl;
29799         
29800         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
29801         var labelWidth = 100;
29802         
29803         if ((this.labelAlign != 'top')) {
29804             if (typeof this.labelWidth == 'number') {
29805                 labelWidth = this.labelWidth
29806             }
29807             this.padWidth =  20 + labelWidth;
29808             
29809         }
29810         
29811         Roo.form.Column.superclass.onRender.call(this, ct, position);
29812         if(this.width){
29813             this.el.setWidth(this.width);
29814         }
29815         if(this.height){
29816             this.el.setHeight(this.height);
29817         }
29818     },
29819     
29820     // private
29821     renderField : function(f){
29822         f.fieldEl = this.fieldTpl.append(this.el, [
29823                f.id, f.fieldLabel,
29824                f.labelStyle||this.labelStyle||'',
29825                this.elementStyle||'',
29826                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
29827                f.itemCls||this.itemCls||'',
29828                f.width ? f.width + this.padWidth : 160 + this.padWidth
29829        ],true);
29830     }
29831 });
29832  
29833
29834 /**
29835  * @class Roo.form.FieldSet
29836  * @extends Roo.form.Layout
29837  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
29838  * @constructor
29839  * @param {Object} config Configuration options
29840  */
29841 Roo.form.FieldSet = function(config){
29842     Roo.form.FieldSet.superclass.constructor.call(this, config);
29843 };
29844
29845 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
29846     /**
29847      * @cfg {String} legend
29848      * The text to display as the legend for the FieldSet (defaults to '')
29849      */
29850     /**
29851      * @cfg {String/Object} autoCreate
29852      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
29853      */
29854
29855     // private
29856     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
29857
29858     // private
29859     onRender : function(ct, position){
29860         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
29861         if(this.legend){
29862             this.setLegend(this.legend);
29863         }
29864     },
29865
29866     // private
29867     setLegend : function(text){
29868         if(this.rendered){
29869             this.el.child('legend').update(text);
29870         }
29871     }
29872 });/*
29873  * Based on:
29874  * Ext JS Library 1.1.1
29875  * Copyright(c) 2006-2007, Ext JS, LLC.
29876  *
29877  * Originally Released Under LGPL - original licence link has changed is not relivant.
29878  *
29879  * Fork - LGPL
29880  * <script type="text/javascript">
29881  */
29882 /**
29883  * @class Roo.form.VTypes
29884  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
29885  * @singleton
29886  */
29887 Roo.form.VTypes = function(){
29888     // closure these in so they are only created once.
29889     var alpha = /^[a-zA-Z_]+$/;
29890     var alphanum = /^[a-zA-Z0-9_]+$/;
29891     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
29892     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
29893
29894     // All these messages and functions are configurable
29895     return {
29896         /**
29897          * The function used to validate email addresses
29898          * @param {String} value The email address
29899          */
29900         'email' : function(v){
29901             return email.test(v);
29902         },
29903         /**
29904          * The error text to display when the email validation function returns false
29905          * @type String
29906          */
29907         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
29908         /**
29909          * The keystroke filter mask to be applied on email input
29910          * @type RegExp
29911          */
29912         'emailMask' : /[a-z0-9_\.\-@]/i,
29913
29914         /**
29915          * The function used to validate URLs
29916          * @param {String} value The URL
29917          */
29918         'url' : function(v){
29919             return url.test(v);
29920         },
29921         /**
29922          * The error text to display when the url validation function returns false
29923          * @type String
29924          */
29925         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
29926         
29927         /**
29928          * The function used to validate alpha values
29929          * @param {String} value The value
29930          */
29931         'alpha' : function(v){
29932             return alpha.test(v);
29933         },
29934         /**
29935          * The error text to display when the alpha validation function returns false
29936          * @type String
29937          */
29938         'alphaText' : 'This field should only contain letters and _',
29939         /**
29940          * The keystroke filter mask to be applied on alpha input
29941          * @type RegExp
29942          */
29943         'alphaMask' : /[a-z_]/i,
29944
29945         /**
29946          * The function used to validate alphanumeric values
29947          * @param {String} value The value
29948          */
29949         'alphanum' : function(v){
29950             return alphanum.test(v);
29951         },
29952         /**
29953          * The error text to display when the alphanumeric validation function returns false
29954          * @type String
29955          */
29956         'alphanumText' : 'This field should only contain letters, numbers and _',
29957         /**
29958          * The keystroke filter mask to be applied on alphanumeric input
29959          * @type RegExp
29960          */
29961         'alphanumMask' : /[a-z0-9_]/i
29962     };
29963 }();//<script type="text/javascript">
29964
29965 /**
29966  * @class Roo.form.FCKeditor
29967  * @extends Roo.form.TextArea
29968  * Wrapper around the FCKEditor http://www.fckeditor.net
29969  * @constructor
29970  * Creates a new FCKeditor
29971  * @param {Object} config Configuration options
29972  */
29973 Roo.form.FCKeditor = function(config){
29974     Roo.form.FCKeditor.superclass.constructor.call(this, config);
29975     this.addEvents({
29976          /**
29977          * @event editorinit
29978          * Fired when the editor is initialized - you can add extra handlers here..
29979          * @param {FCKeditor} this
29980          * @param {Object} the FCK object.
29981          */
29982         editorinit : true
29983     });
29984     
29985     
29986 };
29987 Roo.form.FCKeditor.editors = { };
29988 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
29989 {
29990     //defaultAutoCreate : {
29991     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
29992     //},
29993     // private
29994     /**
29995      * @cfg {Object} fck options - see fck manual for details.
29996      */
29997     fckconfig : false,
29998     
29999     /**
30000      * @cfg {Object} fck toolbar set (Basic or Default)
30001      */
30002     toolbarSet : 'Basic',
30003     /**
30004      * @cfg {Object} fck BasePath
30005      */ 
30006     basePath : '/fckeditor/',
30007     
30008     
30009     frame : false,
30010     
30011     value : '',
30012     
30013    
30014     onRender : function(ct, position)
30015     {
30016         if(!this.el){
30017             this.defaultAutoCreate = {
30018                 tag: "textarea",
30019                 style:"width:300px;height:60px;",
30020                 autocomplete: "off"
30021             };
30022         }
30023         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
30024         /*
30025         if(this.grow){
30026             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
30027             if(this.preventScrollbars){
30028                 this.el.setStyle("overflow", "hidden");
30029             }
30030             this.el.setHeight(this.growMin);
30031         }
30032         */
30033         //console.log('onrender' + this.getId() );
30034         Roo.form.FCKeditor.editors[this.getId()] = this;
30035          
30036
30037         this.replaceTextarea() ;
30038         
30039     },
30040     
30041     getEditor : function() {
30042         return this.fckEditor;
30043     },
30044     /**
30045      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
30046      * @param {Mixed} value The value to set
30047      */
30048     
30049     
30050     setValue : function(value)
30051     {
30052         //console.log('setValue: ' + value);
30053         
30054         if(typeof(value) == 'undefined') { // not sure why this is happending...
30055             return;
30056         }
30057         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
30058         
30059         //if(!this.el || !this.getEditor()) {
30060         //    this.value = value;
30061             //this.setValue.defer(100,this,[value]);    
30062         //    return;
30063         //} 
30064         
30065         if(!this.getEditor()) {
30066             return;
30067         }
30068         
30069         this.getEditor().SetData(value);
30070         
30071         //
30072
30073     },
30074
30075     /**
30076      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
30077      * @return {Mixed} value The field value
30078      */
30079     getValue : function()
30080     {
30081         
30082         if (this.frame && this.frame.dom.style.display == 'none') {
30083             return Roo.form.FCKeditor.superclass.getValue.call(this);
30084         }
30085         
30086         if(!this.el || !this.getEditor()) {
30087            
30088            // this.getValue.defer(100,this); 
30089             return this.value;
30090         }
30091        
30092         
30093         var value=this.getEditor().GetData();
30094         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
30095         return Roo.form.FCKeditor.superclass.getValue.call(this);
30096         
30097
30098     },
30099
30100     /**
30101      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
30102      * @return {Mixed} value The field value
30103      */
30104     getRawValue : function()
30105     {
30106         if (this.frame && this.frame.dom.style.display == 'none') {
30107             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30108         }
30109         
30110         if(!this.el || !this.getEditor()) {
30111             //this.getRawValue.defer(100,this); 
30112             return this.value;
30113             return;
30114         }
30115         
30116         
30117         
30118         var value=this.getEditor().GetData();
30119         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
30120         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30121          
30122     },
30123     
30124     setSize : function(w,h) {
30125         
30126         
30127         
30128         //if (this.frame && this.frame.dom.style.display == 'none') {
30129         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30130         //    return;
30131         //}
30132         //if(!this.el || !this.getEditor()) {
30133         //    this.setSize.defer(100,this, [w,h]); 
30134         //    return;
30135         //}
30136         
30137         
30138         
30139         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30140         
30141         this.frame.dom.setAttribute('width', w);
30142         this.frame.dom.setAttribute('height', h);
30143         this.frame.setSize(w,h);
30144         
30145     },
30146     
30147     toggleSourceEdit : function(value) {
30148         
30149       
30150          
30151         this.el.dom.style.display = value ? '' : 'none';
30152         this.frame.dom.style.display = value ?  'none' : '';
30153         
30154     },
30155     
30156     
30157     focus: function(tag)
30158     {
30159         if (this.frame.dom.style.display == 'none') {
30160             return Roo.form.FCKeditor.superclass.focus.call(this);
30161         }
30162         if(!this.el || !this.getEditor()) {
30163             this.focus.defer(100,this, [tag]); 
30164             return;
30165         }
30166         
30167         
30168         
30169         
30170         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
30171         this.getEditor().Focus();
30172         if (tgs.length) {
30173             if (!this.getEditor().Selection.GetSelection()) {
30174                 this.focus.defer(100,this, [tag]); 
30175                 return;
30176             }
30177             
30178             
30179             var r = this.getEditor().EditorDocument.createRange();
30180             r.setStart(tgs[0],0);
30181             r.setEnd(tgs[0],0);
30182             this.getEditor().Selection.GetSelection().removeAllRanges();
30183             this.getEditor().Selection.GetSelection().addRange(r);
30184             this.getEditor().Focus();
30185         }
30186         
30187     },
30188     
30189     
30190     
30191     replaceTextarea : function()
30192     {
30193         if ( document.getElementById( this.getId() + '___Frame' ) )
30194             return ;
30195         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
30196         //{
30197             // We must check the elements firstly using the Id and then the name.
30198         var oTextarea = document.getElementById( this.getId() );
30199         
30200         var colElementsByName = document.getElementsByName( this.getId() ) ;
30201          
30202         oTextarea.style.display = 'none' ;
30203
30204         if ( oTextarea.tabIndex ) {            
30205             this.TabIndex = oTextarea.tabIndex ;
30206         }
30207         
30208         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
30209         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
30210         this.frame = Roo.get(this.getId() + '___Frame')
30211     },
30212     
30213     _getConfigHtml : function()
30214     {
30215         var sConfig = '' ;
30216
30217         for ( var o in this.fckconfig ) {
30218             sConfig += sConfig.length > 0  ? '&amp;' : '';
30219             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
30220         }
30221
30222         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
30223     },
30224     
30225     
30226     _getIFrameHtml : function()
30227     {
30228         var sFile = 'fckeditor.html' ;
30229         /* no idea what this is about..
30230         try
30231         {
30232             if ( (/fcksource=true/i).test( window.top.location.search ) )
30233                 sFile = 'fckeditor.original.html' ;
30234         }
30235         catch (e) { 
30236         */
30237
30238         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
30239         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
30240         
30241         
30242         var html = '<iframe id="' + this.getId() +
30243             '___Frame" src="' + sLink +
30244             '" width="' + this.width +
30245             '" height="' + this.height + '"' +
30246             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
30247             ' frameborder="0" scrolling="no"></iframe>' ;
30248
30249         return html ;
30250     },
30251     
30252     _insertHtmlBefore : function( html, element )
30253     {
30254         if ( element.insertAdjacentHTML )       {
30255             // IE
30256             element.insertAdjacentHTML( 'beforeBegin', html ) ;
30257         } else { // Gecko
30258             var oRange = document.createRange() ;
30259             oRange.setStartBefore( element ) ;
30260             var oFragment = oRange.createContextualFragment( html );
30261             element.parentNode.insertBefore( oFragment, element ) ;
30262         }
30263     }
30264     
30265     
30266   
30267     
30268     
30269     
30270     
30271
30272 });
30273
30274 //Roo.reg('fckeditor', Roo.form.FCKeditor);
30275
30276 function FCKeditor_OnComplete(editorInstance){
30277     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
30278     f.fckEditor = editorInstance;
30279     //console.log("loaded");
30280     f.fireEvent('editorinit', f, editorInstance);
30281
30282   
30283
30284  
30285
30286
30287
30288
30289
30290
30291
30292
30293
30294
30295
30296
30297
30298
30299
30300 //<script type="text/javascript">
30301 /**
30302  * @class Roo.form.GridField
30303  * @extends Roo.form.Field
30304  * Embed a grid (or editable grid into a form)
30305  * STATUS ALPHA
30306  * 
30307  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
30308  * it needs 
30309  * xgrid.store = Roo.data.Store
30310  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
30311  * xgrid.store.reader = Roo.data.JsonReader 
30312  * 
30313  * 
30314  * @constructor
30315  * Creates a new GridField
30316  * @param {Object} config Configuration options
30317  */
30318 Roo.form.GridField = function(config){
30319     Roo.form.GridField.superclass.constructor.call(this, config);
30320      
30321 };
30322
30323 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
30324     /**
30325      * @cfg {Number} width  - used to restrict width of grid..
30326      */
30327     width : 100,
30328     /**
30329      * @cfg {Number} height - used to restrict height of grid..
30330      */
30331     height : 50,
30332      /**
30333      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
30334          * 
30335          *}
30336      */
30337     xgrid : false, 
30338     /**
30339      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30340      * {tag: "input", type: "checkbox", autocomplete: "off"})
30341      */
30342    // defaultAutoCreate : { tag: 'div' },
30343     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30344     /**
30345      * @cfg {String} addTitle Text to include for adding a title.
30346      */
30347     addTitle : false,
30348     //
30349     onResize : function(){
30350         Roo.form.Field.superclass.onResize.apply(this, arguments);
30351     },
30352
30353     initEvents : function(){
30354         // Roo.form.Checkbox.superclass.initEvents.call(this);
30355         // has no events...
30356        
30357     },
30358
30359
30360     getResizeEl : function(){
30361         return this.wrap;
30362     },
30363
30364     getPositionEl : function(){
30365         return this.wrap;
30366     },
30367
30368     // private
30369     onRender : function(ct, position){
30370         
30371         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
30372         var style = this.style;
30373         delete this.style;
30374         
30375         Roo.form.GridField.superclass.onRender.call(this, ct, position);
30376         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
30377         this.viewEl = this.wrap.createChild({ tag: 'div' });
30378         if (style) {
30379             this.viewEl.applyStyles(style);
30380         }
30381         if (this.width) {
30382             this.viewEl.setWidth(this.width);
30383         }
30384         if (this.height) {
30385             this.viewEl.setHeight(this.height);
30386         }
30387         //if(this.inputValue !== undefined){
30388         //this.setValue(this.value);
30389         
30390         
30391         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
30392         
30393         
30394         this.grid.render();
30395         this.grid.getDataSource().on('remove', this.refreshValue, this);
30396         this.grid.getDataSource().on('update', this.refreshValue, this);
30397         this.grid.on('afteredit', this.refreshValue, this);
30398  
30399     },
30400      
30401     
30402     /**
30403      * Sets the value of the item. 
30404      * @param {String} either an object  or a string..
30405      */
30406     setValue : function(v){
30407         //this.value = v;
30408         v = v || []; // empty set..
30409         // this does not seem smart - it really only affects memoryproxy grids..
30410         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
30411             var ds = this.grid.getDataSource();
30412             // assumes a json reader..
30413             var data = {}
30414             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
30415             ds.loadData( data);
30416         }
30417         // clear selection so it does not get stale.
30418         if (this.grid.sm) { 
30419             this.grid.sm.clearSelections();
30420         }
30421         
30422         Roo.form.GridField.superclass.setValue.call(this, v);
30423         this.refreshValue();
30424         // should load data in the grid really....
30425     },
30426     
30427     // private
30428     refreshValue: function() {
30429          var val = [];
30430         this.grid.getDataSource().each(function(r) {
30431             val.push(r.data);
30432         });
30433         this.el.dom.value = Roo.encode(val);
30434     }
30435     
30436      
30437     
30438     
30439 });/*
30440  * Based on:
30441  * Ext JS Library 1.1.1
30442  * Copyright(c) 2006-2007, Ext JS, LLC.
30443  *
30444  * Originally Released Under LGPL - original licence link has changed is not relivant.
30445  *
30446  * Fork - LGPL
30447  * <script type="text/javascript">
30448  */
30449 /**
30450  * @class Roo.form.DisplayField
30451  * @extends Roo.form.Field
30452  * A generic Field to display non-editable data.
30453  * @constructor
30454  * Creates a new Display Field item.
30455  * @param {Object} config Configuration options
30456  */
30457 Roo.form.DisplayField = function(config){
30458     Roo.form.DisplayField.superclass.constructor.call(this, config);
30459     
30460 };
30461
30462 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
30463     inputType:      'hidden',
30464     allowBlank:     true,
30465     readOnly:         true,
30466     
30467  
30468     /**
30469      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30470      */
30471     focusClass : undefined,
30472     /**
30473      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30474      */
30475     fieldClass: 'x-form-field',
30476     
30477      /**
30478      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
30479      */
30480     valueRenderer: undefined,
30481     
30482     width: 100,
30483     /**
30484      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30485      * {tag: "input", type: "checkbox", autocomplete: "off"})
30486      */
30487      
30488  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30489
30490     onResize : function(){
30491         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
30492         
30493     },
30494
30495     initEvents : function(){
30496         // Roo.form.Checkbox.superclass.initEvents.call(this);
30497         // has no events...
30498        
30499     },
30500
30501
30502     getResizeEl : function(){
30503         return this.wrap;
30504     },
30505
30506     getPositionEl : function(){
30507         return this.wrap;
30508     },
30509
30510     // private
30511     onRender : function(ct, position){
30512         
30513         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
30514         //if(this.inputValue !== undefined){
30515         this.wrap = this.el.wrap();
30516         
30517         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
30518         
30519         if (this.bodyStyle) {
30520             this.viewEl.applyStyles(this.bodyStyle);
30521         }
30522         //this.viewEl.setStyle('padding', '2px');
30523         
30524         this.setValue(this.value);
30525         
30526     },
30527 /*
30528     // private
30529     initValue : Roo.emptyFn,
30530
30531   */
30532
30533         // private
30534     onClick : function(){
30535         
30536     },
30537
30538     /**
30539      * Sets the checked state of the checkbox.
30540      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
30541      */
30542     setValue : function(v){
30543         this.value = v;
30544         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
30545         // this might be called before we have a dom element..
30546         if (!this.viewEl) {
30547             return;
30548         }
30549         this.viewEl.dom.innerHTML = html;
30550         Roo.form.DisplayField.superclass.setValue.call(this, v);
30551
30552     }
30553 });/*
30554  * 
30555  * Licence- LGPL
30556  * 
30557  */
30558
30559 /**
30560  * @class Roo.form.DayPicker
30561  * @extends Roo.form.Field
30562  * A Day picker show [M] [T] [W] ....
30563  * @constructor
30564  * Creates a new Day Picker
30565  * @param {Object} config Configuration options
30566  */
30567 Roo.form.DayPicker= function(config){
30568     Roo.form.DayPicker.superclass.constructor.call(this, config);
30569      
30570 };
30571
30572 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
30573     /**
30574      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30575      */
30576     focusClass : undefined,
30577     /**
30578      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30579      */
30580     fieldClass: "x-form-field",
30581    
30582     /**
30583      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30584      * {tag: "input", type: "checkbox", autocomplete: "off"})
30585      */
30586     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
30587     
30588    
30589     actionMode : 'viewEl', 
30590     //
30591     // private
30592  
30593     inputType : 'hidden',
30594     
30595      
30596     inputElement: false, // real input element?
30597     basedOn: false, // ????
30598     
30599     isFormField: true, // not sure where this is needed!!!!
30600
30601     onResize : function(){
30602         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
30603         if(!this.boxLabel){
30604             this.el.alignTo(this.wrap, 'c-c');
30605         }
30606     },
30607
30608     initEvents : function(){
30609         Roo.form.Checkbox.superclass.initEvents.call(this);
30610         this.el.on("click", this.onClick,  this);
30611         this.el.on("change", this.onClick,  this);
30612     },
30613
30614
30615     getResizeEl : function(){
30616         return this.wrap;
30617     },
30618
30619     getPositionEl : function(){
30620         return this.wrap;
30621     },
30622
30623     
30624     // private
30625     onRender : function(ct, position){
30626         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
30627        
30628         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
30629         
30630         var r1 = '<table><tr>';
30631         var r2 = '<tr class="x-form-daypick-icons">';
30632         for (var i=0; i < 7; i++) {
30633             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
30634             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
30635         }
30636         
30637         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
30638         viewEl.select('img').on('click', this.onClick, this);
30639         this.viewEl = viewEl;   
30640         
30641         
30642         // this will not work on Chrome!!!
30643         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
30644         this.el.on('propertychange', this.setFromHidden,  this);  //ie
30645         
30646         
30647           
30648
30649     },
30650
30651     // private
30652     initValue : Roo.emptyFn,
30653
30654     /**
30655      * Returns the checked state of the checkbox.
30656      * @return {Boolean} True if checked, else false
30657      */
30658     getValue : function(){
30659         return this.el.dom.value;
30660         
30661     },
30662
30663         // private
30664     onClick : function(e){ 
30665         //this.setChecked(!this.checked);
30666         Roo.get(e.target).toggleClass('x-menu-item-checked');
30667         this.refreshValue();
30668         //if(this.el.dom.checked != this.checked){
30669         //    this.setValue(this.el.dom.checked);
30670        // }
30671     },
30672     
30673     // private
30674     refreshValue : function()
30675     {
30676         var val = '';
30677         this.viewEl.select('img',true).each(function(e,i,n)  {
30678             val += e.is(".x-menu-item-checked") ? String(n) : '';
30679         });
30680         this.setValue(val, true);
30681     },
30682
30683     /**
30684      * Sets the checked state of the checkbox.
30685      * On is always based on a string comparison between inputValue and the param.
30686      * @param {Boolean/String} value - the value to set 
30687      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
30688      */
30689     setValue : function(v,suppressEvent){
30690         if (!this.el.dom) {
30691             return;
30692         }
30693         var old = this.el.dom.value ;
30694         this.el.dom.value = v;
30695         if (suppressEvent) {
30696             return ;
30697         }
30698          
30699         // update display..
30700         this.viewEl.select('img',true).each(function(e,i,n)  {
30701             
30702             var on = e.is(".x-menu-item-checked");
30703             var newv = v.indexOf(String(n)) > -1;
30704             if (on != newv) {
30705                 e.toggleClass('x-menu-item-checked');
30706             }
30707             
30708         });
30709         
30710         
30711         this.fireEvent('change', this, v, old);
30712         
30713         
30714     },
30715    
30716     // handle setting of hidden value by some other method!!?!?
30717     setFromHidden: function()
30718     {
30719         if(!this.el){
30720             return;
30721         }
30722         //console.log("SET FROM HIDDEN");
30723         //alert('setFrom hidden');
30724         this.setValue(this.el.dom.value);
30725     },
30726     
30727     onDestroy : function()
30728     {
30729         if(this.viewEl){
30730             Roo.get(this.viewEl).remove();
30731         }
30732          
30733         Roo.form.DayPicker.superclass.onDestroy.call(this);
30734     }
30735
30736 });/*
30737  * RooJS Library 1.1.1
30738  * Copyright(c) 2008-2011  Alan Knowles
30739  *
30740  * License - LGPL
30741  */
30742  
30743
30744 /**
30745  * @class Roo.form.ComboCheck
30746  * @extends Roo.form.ComboBox
30747  * A combobox for multiple select items.
30748  *
30749  * FIXME - could do with a reset button..
30750  * 
30751  * @constructor
30752  * Create a new ComboCheck
30753  * @param {Object} config Configuration options
30754  */
30755 Roo.form.ComboCheck = function(config){
30756     Roo.form.ComboCheck.superclass.constructor.call(this, config);
30757     // should verify some data...
30758     // like
30759     // hiddenName = required..
30760     // displayField = required
30761     // valudField == required
30762     var req= [ 'hiddenName', 'displayField', 'valueField' ];
30763     var _t = this;
30764     Roo.each(req, function(e) {
30765         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
30766             throw "Roo.form.ComboCheck : missing value for: " + e;
30767         }
30768     });
30769     
30770     
30771 };
30772
30773 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
30774      
30775      
30776     editable : false,
30777      
30778     selectedClass: 'x-menu-item-checked', 
30779     
30780     // private
30781     onRender : function(ct, position){
30782         var _t = this;
30783         
30784         
30785         
30786         if(!this.tpl){
30787             var cls = 'x-combo-list';
30788
30789             
30790             this.tpl =  new Roo.Template({
30791                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
30792                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
30793                    '<span>{' + this.displayField + '}</span>' +
30794                     '</div>' 
30795                 
30796             });
30797         }
30798  
30799         
30800         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
30801         this.view.singleSelect = false;
30802         this.view.multiSelect = true;
30803         this.view.toggleSelect = true;
30804         this.pageTb.add(new Roo.Toolbar.Fill(), {
30805             
30806             text: 'Done',
30807             handler: function()
30808             {
30809                 _t.collapse();
30810             }
30811         });
30812     },
30813     
30814     onViewOver : function(e, t){
30815         // do nothing...
30816         return;
30817         
30818     },
30819     
30820     onViewClick : function(doFocus,index){
30821         return;
30822         
30823     },
30824     select: function () {
30825         //Roo.log("SELECT CALLED");
30826     },
30827      
30828     selectByValue : function(xv, scrollIntoView){
30829         var ar = this.getValueArray();
30830         var sels = [];
30831         
30832         Roo.each(ar, function(v) {
30833             if(v === undefined || v === null){
30834                 return;
30835             }
30836             var r = this.findRecord(this.valueField, v);
30837             if(r){
30838                 sels.push(this.store.indexOf(r))
30839                 
30840             }
30841         },this);
30842         this.view.select(sels);
30843         return false;
30844     },
30845     
30846     
30847     
30848     onSelect : function(record, index){
30849        // Roo.log("onselect Called");
30850        // this is only called by the clear button now..
30851         this.view.clearSelections();
30852         this.setValue('[]');
30853         if (this.value != this.valueBefore) {
30854             this.fireEvent('change', this, this.value, this.valueBefore);
30855             this.valueBefore = this.value;
30856         }
30857     },
30858     getValueArray : function()
30859     {
30860         var ar = [] ;
30861         
30862         try {
30863             //Roo.log(this.value);
30864             if (typeof(this.value) == 'undefined') {
30865                 return [];
30866             }
30867             var ar = Roo.decode(this.value);
30868             return  ar instanceof Array ? ar : []; //?? valid?
30869             
30870         } catch(e) {
30871             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
30872             return [];
30873         }
30874          
30875     },
30876     expand : function ()
30877     {
30878         
30879         Roo.form.ComboCheck.superclass.expand.call(this);
30880         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
30881         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
30882         
30883
30884     },
30885     
30886     collapse : function(){
30887         Roo.form.ComboCheck.superclass.collapse.call(this);
30888         var sl = this.view.getSelectedIndexes();
30889         var st = this.store;
30890         var nv = [];
30891         var tv = [];
30892         var r;
30893         Roo.each(sl, function(i) {
30894             r = st.getAt(i);
30895             nv.push(r.get(this.valueField));
30896         },this);
30897         this.setValue(Roo.encode(nv));
30898         if (this.value != this.valueBefore) {
30899
30900             this.fireEvent('change', this, this.value, this.valueBefore);
30901             this.valueBefore = this.value;
30902         }
30903         
30904     },
30905     
30906     setValue : function(v){
30907         // Roo.log(v);
30908         this.value = v;
30909         
30910         var vals = this.getValueArray();
30911         var tv = [];
30912         Roo.each(vals, function(k) {
30913             var r = this.findRecord(this.valueField, k);
30914             if(r){
30915                 tv.push(r.data[this.displayField]);
30916             }else if(this.valueNotFoundText !== undefined){
30917                 tv.push( this.valueNotFoundText );
30918             }
30919         },this);
30920        // Roo.log(tv);
30921         
30922         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
30923         this.hiddenField.value = v;
30924         this.value = v;
30925     }
30926     
30927 });/*
30928  * Based on:
30929  * Ext JS Library 1.1.1
30930  * Copyright(c) 2006-2007, Ext JS, LLC.
30931  *
30932  * Originally Released Under LGPL - original licence link has changed is not relivant.
30933  *
30934  * Fork - LGPL
30935  * <script type="text/javascript">
30936  */
30937  
30938 /**
30939  * @class Roo.form.Signature
30940  * @extends Roo.form.Field
30941  * Signature field.  
30942  * @constructor
30943  * 
30944  * @param {Object} config Configuration options
30945  */
30946
30947 Roo.form.Signature = function(config){
30948     Roo.form.Signature.superclass.constructor.call(this, config);
30949     
30950     this.addEvents({// not in used??
30951          /**
30952          * @event confirm
30953          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
30954              * @param {Roo.form.Signature} combo This combo box
30955              */
30956         'confirm' : true,
30957         /**
30958          * @event reset
30959          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
30960              * @param {Roo.form.ComboBox} combo This combo box
30961              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
30962              */
30963         'reset' : true
30964     });
30965 };
30966
30967 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
30968     /**
30969      * @cfg {Object} labels Label to use when rendering a form.
30970      * defaults to 
30971      * labels : { 
30972      *      clear : "Clear",
30973      *      confirm : "Confirm"
30974      *  }
30975      */
30976     labels : { 
30977         clear : "Clear",
30978         confirm : "Confirm"
30979     },
30980     /**
30981      * @cfg {Number} width The signature panel width (defaults to 300)
30982      */
30983     width: 300,
30984     /**
30985      * @cfg {Number} height The signature panel height (defaults to 100)
30986      */
30987     height : 100,
30988     /**
30989      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
30990      */
30991     allowBlank : false,
30992     
30993     //private
30994     // {Object} signPanel The signature SVG panel element (defaults to {})
30995     signPanel : {},
30996     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
30997     isMouseDown : false,
30998     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
30999     isConfirmed : false,
31000     // {String} signatureTmp SVG mapping string (defaults to empty string)
31001     signatureTmp : '',
31002     
31003     
31004     defaultAutoCreate : { // modified by initCompnoent..
31005         tag: "input",
31006         type:"hidden"
31007     },
31008
31009     // private
31010     onRender : function(ct, position){
31011         
31012         Roo.form.Signature.superclass.onRender.call(this, ct, position);
31013         
31014         this.wrap = this.el.wrap({
31015             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
31016         });
31017         
31018         this.createToolbar(this);
31019         this.signPanel = this.wrap.createChild({
31020                 tag: 'div',
31021                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
31022             }, this.el
31023         );
31024             
31025         this.svgID = Roo.id();
31026         this.svgEl = this.signPanel.createChild({
31027               xmlns : 'http://www.w3.org/2000/svg',
31028               tag : 'svg',
31029               id : this.svgID + "-svg",
31030               width: this.width,
31031               height: this.height,
31032               viewBox: '0 0 '+this.width+' '+this.height,
31033               cn : [
31034                 {
31035                     tag: "rect",
31036                     id: this.svgID + "-svg-r",
31037                     width: this.width,
31038                     height: this.height,
31039                     fill: "#ffa"
31040                 },
31041                 {
31042                     tag: "line",
31043                     id: this.svgID + "-svg-l",
31044                     x1: "0", // start
31045                     y1: (this.height*0.8), // start set the line in 80% of height
31046                     x2: this.width, // end
31047                     y2: (this.height*0.8), // end set the line in 80% of height
31048                     'stroke': "#666",
31049                     'stroke-width': "1",
31050                     'stroke-dasharray': "3",
31051                     'shape-rendering': "crispEdges",
31052                     'pointer-events': "none"
31053                 },
31054                 {
31055                     tag: "path",
31056                     id: this.svgID + "-svg-p",
31057                     'stroke': "navy",
31058                     'stroke-width': "3",
31059                     'fill': "none",
31060                     'pointer-events': 'none'
31061                 }
31062               ]
31063         });
31064         this.createSVG();
31065         this.svgBox = this.svgEl.dom.getScreenCTM();
31066     },
31067     createSVG : function(){ 
31068         var svg = this.signPanel;
31069         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
31070         var t = this;
31071
31072         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
31073         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
31074         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
31075         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
31076         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
31077         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
31078         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
31079         
31080     },
31081     isTouchEvent : function(e){
31082         return e.type.match(/^touch/);
31083     },
31084     getCoords : function (e) {
31085         var pt    = this.svgEl.dom.createSVGPoint();
31086         pt.x = e.clientX; 
31087         pt.y = e.clientY;
31088         if (this.isTouchEvent(e)) {
31089             pt.x =  e.targetTouches[0].clientX 
31090             pt.y = e.targetTouches[0].clientY;
31091         }
31092         var a = this.svgEl.dom.getScreenCTM();
31093         var b = a.inverse();
31094         var mx = pt.matrixTransform(b);
31095         return mx.x + ',' + mx.y;
31096     },
31097     //mouse event headler 
31098     down : function (e) {
31099         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
31100         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
31101         
31102         this.isMouseDown = true;
31103         
31104         e.preventDefault();
31105     },
31106     move : function (e) {
31107         if (this.isMouseDown) {
31108             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
31109             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
31110         }
31111         
31112         e.preventDefault();
31113     },
31114     up : function (e) {
31115         this.isMouseDown = false;
31116         var sp = this.signatureTmp.split(' ');
31117         
31118         if(sp.length > 1){
31119             if(!sp[sp.length-2].match(/^L/)){
31120                 sp.pop();
31121                 sp.pop();
31122                 sp.push("");
31123                 this.signatureTmp = sp.join(" ");
31124             }
31125         }
31126         if(this.getValue() != this.signatureTmp){
31127             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31128             this.isConfirmed = false;
31129         }
31130         e.preventDefault();
31131     },
31132     
31133     /**
31134      * Protected method that will not generally be called directly. It
31135      * is called when the editor creates its toolbar. Override this method if you need to
31136      * add custom toolbar buttons.
31137      * @param {HtmlEditor} editor
31138      */
31139     createToolbar : function(editor){
31140          function btn(id, toggle, handler){
31141             var xid = fid + '-'+ id ;
31142             return {
31143                 id : xid,
31144                 cmd : id,
31145                 cls : 'x-btn-icon x-edit-'+id,
31146                 enableToggle:toggle !== false,
31147                 scope: editor, // was editor...
31148                 handler:handler||editor.relayBtnCmd,
31149                 clickEvent:'mousedown',
31150                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
31151                 tabIndex:-1
31152             };
31153         }
31154         
31155         
31156         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
31157         this.tb = tb;
31158         this.tb.add(
31159            {
31160                 cls : ' x-signature-btn x-signature-'+id,
31161                 scope: editor, // was editor...
31162                 handler: this.reset,
31163                 clickEvent:'mousedown',
31164                 text: this.labels.clear
31165             },
31166             {
31167                  xtype : 'Fill',
31168                  xns: Roo.Toolbar
31169             }, 
31170             {
31171                 cls : '  x-signature-btn x-signature-'+id,
31172                 scope: editor, // was editor...
31173                 handler: this.confirmHandler,
31174                 clickEvent:'mousedown',
31175                 text: this.labels.confirm
31176             }
31177         );
31178     
31179     },
31180     //public
31181     /**
31182      * when user is clicked confirm then show this image.....
31183      * 
31184      * @return {String} Image Data URI
31185      */
31186     getImageDataURI : function(){
31187         var svg = this.svgEl.dom.parentNode.innerHTML;
31188         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
31189         return src; 
31190     },
31191     /**
31192      * 
31193      * @return {Boolean} this.isConfirmed
31194      */
31195     getConfirmed : function(){
31196         return this.isConfirmed;
31197     },
31198     /**
31199      * 
31200      * @return {Number} this.width
31201      */
31202     getWidth : function(){
31203         return this.width;
31204     },
31205     /**
31206      * 
31207      * @return {Number} this.height
31208      */
31209     getHeight : function(){
31210         return this.height;
31211     },
31212     // private
31213     getSignature : function(){
31214         return this.signatureTmp;
31215     },
31216     // private
31217     reset : function(){
31218         this.signatureTmp = '';
31219         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31220         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
31221         this.isConfirmed = false;
31222         Roo.form.Signature.superclass.reset.call(this);
31223     },
31224     setSignature : function(s){
31225         this.signatureTmp = s;
31226         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31227         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
31228         this.setValue(s);
31229         this.isConfirmed = false;
31230         Roo.form.Signature.superclass.reset.call(this);
31231     }, 
31232     test : function(){
31233 //        Roo.log(this.signPanel.dom.contentWindow.up())
31234     },
31235     //private
31236     setConfirmed : function(){
31237         
31238         
31239         
31240 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
31241     },
31242     // private
31243     confirmHandler : function(){
31244         if(!this.getSignature()){
31245             return;
31246         }
31247         
31248         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
31249         this.setValue(this.getSignature());
31250         this.isConfirmed = true;
31251         
31252         this.fireEvent('confirm', this);
31253     },
31254     // private
31255     // Subclasses should provide the validation implementation by overriding this
31256     validateValue : function(value){
31257         if(this.allowBlank){
31258             return true;
31259         }
31260         
31261         if(this.isConfirmed){
31262             return true;
31263         }
31264         return false;
31265     }
31266 });/*
31267  * Based on:
31268  * Ext JS Library 1.1.1
31269  * Copyright(c) 2006-2007, Ext JS, LLC.
31270  *
31271  * Originally Released Under LGPL - original licence link has changed is not relivant.
31272  *
31273  * Fork - LGPL
31274  * <script type="text/javascript">
31275  */
31276  
31277
31278 /**
31279  * @class Roo.form.ComboBox
31280  * @extends Roo.form.TriggerField
31281  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
31282  * @constructor
31283  * Create a new ComboBox.
31284  * @param {Object} config Configuration options
31285  */
31286 Roo.form.Select = function(config){
31287     Roo.form.Select.superclass.constructor.call(this, config);
31288      
31289 };
31290
31291 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
31292     /**
31293      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
31294      */
31295     /**
31296      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
31297      * rendering into an Roo.Editor, defaults to false)
31298      */
31299     /**
31300      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
31301      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
31302      */
31303     /**
31304      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
31305      */
31306     /**
31307      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
31308      * the dropdown list (defaults to undefined, with no header element)
31309      */
31310
31311      /**
31312      * @cfg {String/Roo.Template} tpl The template to use to render the output
31313      */
31314      
31315     // private
31316     defaultAutoCreate : {tag: "select"  },
31317     /**
31318      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
31319      */
31320     listWidth: undefined,
31321     /**
31322      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
31323      * mode = 'remote' or 'text' if mode = 'local')
31324      */
31325     displayField: undefined,
31326     /**
31327      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
31328      * mode = 'remote' or 'value' if mode = 'local'). 
31329      * Note: use of a valueField requires the user make a selection
31330      * in order for a value to be mapped.
31331      */
31332     valueField: undefined,
31333     
31334     
31335     /**
31336      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
31337      * field's data value (defaults to the underlying DOM element's name)
31338      */
31339     hiddenName: undefined,
31340     /**
31341      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
31342      */
31343     listClass: '',
31344     /**
31345      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
31346      */
31347     selectedClass: 'x-combo-selected',
31348     /**
31349      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
31350      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
31351      * which displays a downward arrow icon).
31352      */
31353     triggerClass : 'x-form-arrow-trigger',
31354     /**
31355      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31356      */
31357     shadow:'sides',
31358     /**
31359      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
31360      * anchor positions (defaults to 'tl-bl')
31361      */
31362     listAlign: 'tl-bl?',
31363     /**
31364      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
31365      */
31366     maxHeight: 300,
31367     /**
31368      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
31369      * query specified by the allQuery config option (defaults to 'query')
31370      */
31371     triggerAction: 'query',
31372     /**
31373      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
31374      * (defaults to 4, does not apply if editable = false)
31375      */
31376     minChars : 4,
31377     /**
31378      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
31379      * delay (typeAheadDelay) if it matches a known value (defaults to false)
31380      */
31381     typeAhead: false,
31382     /**
31383      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
31384      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
31385      */
31386     queryDelay: 500,
31387     /**
31388      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
31389      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
31390      */
31391     pageSize: 0,
31392     /**
31393      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
31394      * when editable = true (defaults to false)
31395      */
31396     selectOnFocus:false,
31397     /**
31398      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
31399      */
31400     queryParam: 'query',
31401     /**
31402      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
31403      * when mode = 'remote' (defaults to 'Loading...')
31404      */
31405     loadingText: 'Loading...',
31406     /**
31407      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
31408      */
31409     resizable: false,
31410     /**
31411      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
31412      */
31413     handleHeight : 8,
31414     /**
31415      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
31416      * traditional select (defaults to true)
31417      */
31418     editable: true,
31419     /**
31420      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
31421      */
31422     allQuery: '',
31423     /**
31424      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
31425      */
31426     mode: 'remote',
31427     /**
31428      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
31429      * listWidth has a higher value)
31430      */
31431     minListWidth : 70,
31432     /**
31433      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
31434      * allow the user to set arbitrary text into the field (defaults to false)
31435      */
31436     forceSelection:false,
31437     /**
31438      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
31439      * if typeAhead = true (defaults to 250)
31440      */
31441     typeAheadDelay : 250,
31442     /**
31443      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
31444      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
31445      */
31446     valueNotFoundText : undefined,
31447     
31448     /**
31449      * @cfg {String} defaultValue The value displayed after loading the store.
31450      */
31451     defaultValue: '',
31452     
31453     /**
31454      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
31455      */
31456     blockFocus : false,
31457     
31458     /**
31459      * @cfg {Boolean} disableClear Disable showing of clear button.
31460      */
31461     disableClear : false,
31462     /**
31463      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
31464      */
31465     alwaysQuery : false,
31466     
31467     //private
31468     addicon : false,
31469     editicon: false,
31470     
31471     // element that contains real text value.. (when hidden is used..)
31472      
31473     // private
31474     onRender : function(ct, position){
31475         Roo.form.Field.prototype.onRender.call(this, ct, position);
31476         
31477         if(this.store){
31478             this.store.on('beforeload', this.onBeforeLoad, this);
31479             this.store.on('load', this.onLoad, this);
31480             this.store.on('loadexception', this.onLoadException, this);
31481             this.store.load({});
31482         }
31483         
31484         
31485         
31486     },
31487
31488     // private
31489     initEvents : function(){
31490         //Roo.form.ComboBox.superclass.initEvents.call(this);
31491  
31492     },
31493
31494     onDestroy : function(){
31495        
31496         if(this.store){
31497             this.store.un('beforeload', this.onBeforeLoad, this);
31498             this.store.un('load', this.onLoad, this);
31499             this.store.un('loadexception', this.onLoadException, this);
31500         }
31501         //Roo.form.ComboBox.superclass.onDestroy.call(this);
31502     },
31503
31504     // private
31505     fireKey : function(e){
31506         if(e.isNavKeyPress() && !this.list.isVisible()){
31507             this.fireEvent("specialkey", this, e);
31508         }
31509     },
31510
31511     // private
31512     onResize: function(w, h){
31513         
31514         return; 
31515     
31516         
31517     },
31518
31519     /**
31520      * Allow or prevent the user from directly editing the field text.  If false is passed,
31521      * the user will only be able to select from the items defined in the dropdown list.  This method
31522      * is the runtime equivalent of setting the 'editable' config option at config time.
31523      * @param {Boolean} value True to allow the user to directly edit the field text
31524      */
31525     setEditable : function(value){
31526          
31527     },
31528
31529     // private
31530     onBeforeLoad : function(){
31531         
31532         Roo.log("Select before load");
31533         return;
31534     
31535         this.innerList.update(this.loadingText ?
31536                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
31537         //this.restrictHeight();
31538         this.selectedIndex = -1;
31539     },
31540
31541     // private
31542     onLoad : function(){
31543
31544     
31545         var dom = this.el.dom;
31546         dom.innerHTML = '';
31547          var od = dom.ownerDocument;
31548          
31549         if (this.emptyText) {
31550             var op = od.createElement('option');
31551             op.setAttribute('value', '');
31552             op.innerHTML = String.format('{0}', this.emptyText);
31553             dom.appendChild(op);
31554         }
31555         if(this.store.getCount() > 0){
31556            
31557             var vf = this.valueField;
31558             var df = this.displayField;
31559             this.store.data.each(function(r) {
31560                 // which colmsn to use... testing - cdoe / title..
31561                 var op = od.createElement('option');
31562                 op.setAttribute('value', r.data[vf]);
31563                 op.innerHTML = String.format('{0}', r.data[df]);
31564                 dom.appendChild(op);
31565             });
31566             if (typeof(this.defaultValue != 'undefined')) {
31567                 this.setValue(this.defaultValue);
31568             }
31569             
31570              
31571         }else{
31572             //this.onEmptyResults();
31573         }
31574         //this.el.focus();
31575     },
31576     // private
31577     onLoadException : function()
31578     {
31579         dom.innerHTML = '';
31580             
31581         Roo.log("Select on load exception");
31582         return;
31583     
31584         this.collapse();
31585         Roo.log(this.store.reader.jsonData);
31586         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
31587             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
31588         }
31589         
31590         
31591     },
31592     // private
31593     onTypeAhead : function(){
31594          
31595     },
31596
31597     // private
31598     onSelect : function(record, index){
31599         Roo.log('on select?');
31600         return;
31601         if(this.fireEvent('beforeselect', this, record, index) !== false){
31602             this.setFromData(index > -1 ? record.data : false);
31603             this.collapse();
31604             this.fireEvent('select', this, record, index);
31605         }
31606     },
31607
31608     /**
31609      * Returns the currently selected field value or empty string if no value is set.
31610      * @return {String} value The selected value
31611      */
31612     getValue : function(){
31613         var dom = this.el.dom;
31614         this.value = dom.options[dom.selectedIndex].value;
31615         return this.value;
31616         
31617     },
31618
31619     /**
31620      * Clears any text/value currently set in the field
31621      */
31622     clearValue : function(){
31623         this.value = '';
31624         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
31625         
31626     },
31627
31628     /**
31629      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
31630      * will be displayed in the field.  If the value does not match the data value of an existing item,
31631      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
31632      * Otherwise the field will be blank (although the value will still be set).
31633      * @param {String} value The value to match
31634      */
31635     setValue : function(v){
31636         var d = this.el.dom;
31637         for (var i =0; i < d.options.length;i++) {
31638             if (v == d.options[i].value) {
31639                 d.selectedIndex = i;
31640                 this.value = v;
31641                 return;
31642             }
31643         }
31644         this.clearValue();
31645     },
31646     /**
31647      * @property {Object} the last set data for the element
31648      */
31649     
31650     lastData : false,
31651     /**
31652      * Sets the value of the field based on a object which is related to the record format for the store.
31653      * @param {Object} value the value to set as. or false on reset?
31654      */
31655     setFromData : function(o){
31656         Roo.log('setfrom data?');
31657          
31658         
31659         
31660     },
31661     // private
31662     reset : function(){
31663         this.clearValue();
31664     },
31665     // private
31666     findRecord : function(prop, value){
31667         
31668         return false;
31669     
31670         var record;
31671         if(this.store.getCount() > 0){
31672             this.store.each(function(r){
31673                 if(r.data[prop] == value){
31674                     record = r;
31675                     return false;
31676                 }
31677                 return true;
31678             });
31679         }
31680         return record;
31681     },
31682     
31683     getName: function()
31684     {
31685         // returns hidden if it's set..
31686         if (!this.rendered) {return ''};
31687         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
31688         
31689     },
31690      
31691
31692     
31693
31694     // private
31695     onEmptyResults : function(){
31696         Roo.log('empty results');
31697         //this.collapse();
31698     },
31699
31700     /**
31701      * Returns true if the dropdown list is expanded, else false.
31702      */
31703     isExpanded : function(){
31704         return false;
31705     },
31706
31707     /**
31708      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
31709      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
31710      * @param {String} value The data value of the item to select
31711      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
31712      * selected item if it is not currently in view (defaults to true)
31713      * @return {Boolean} True if the value matched an item in the list, else false
31714      */
31715     selectByValue : function(v, scrollIntoView){
31716         Roo.log('select By Value');
31717         return false;
31718     
31719         if(v !== undefined && v !== null){
31720             var r = this.findRecord(this.valueField || this.displayField, v);
31721             if(r){
31722                 this.select(this.store.indexOf(r), scrollIntoView);
31723                 return true;
31724             }
31725         }
31726         return false;
31727     },
31728
31729     /**
31730      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
31731      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
31732      * @param {Number} index The zero-based index of the list item to select
31733      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
31734      * selected item if it is not currently in view (defaults to true)
31735      */
31736     select : function(index, scrollIntoView){
31737         Roo.log('select ');
31738         return  ;
31739         
31740         this.selectedIndex = index;
31741         this.view.select(index);
31742         if(scrollIntoView !== false){
31743             var el = this.view.getNode(index);
31744             if(el){
31745                 this.innerList.scrollChildIntoView(el, false);
31746             }
31747         }
31748     },
31749
31750       
31751
31752     // private
31753     validateBlur : function(){
31754         
31755         return;
31756         
31757     },
31758
31759     // private
31760     initQuery : function(){
31761         this.doQuery(this.getRawValue());
31762     },
31763
31764     // private
31765     doForce : function(){
31766         if(this.el.dom.value.length > 0){
31767             this.el.dom.value =
31768                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
31769              
31770         }
31771     },
31772
31773     /**
31774      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
31775      * query allowing the query action to be canceled if needed.
31776      * @param {String} query The SQL query to execute
31777      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
31778      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
31779      * saved in the current store (defaults to false)
31780      */
31781     doQuery : function(q, forceAll){
31782         
31783         Roo.log('doQuery?');
31784         if(q === undefined || q === null){
31785             q = '';
31786         }
31787         var qe = {
31788             query: q,
31789             forceAll: forceAll,
31790             combo: this,
31791             cancel:false
31792         };
31793         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
31794             return false;
31795         }
31796         q = qe.query;
31797         forceAll = qe.forceAll;
31798         if(forceAll === true || (q.length >= this.minChars)){
31799             if(this.lastQuery != q || this.alwaysQuery){
31800                 this.lastQuery = q;
31801                 if(this.mode == 'local'){
31802                     this.selectedIndex = -1;
31803                     if(forceAll){
31804                         this.store.clearFilter();
31805                     }else{
31806                         this.store.filter(this.displayField, q);
31807                     }
31808                     this.onLoad();
31809                 }else{
31810                     this.store.baseParams[this.queryParam] = q;
31811                     this.store.load({
31812                         params: this.getParams(q)
31813                     });
31814                     this.expand();
31815                 }
31816             }else{
31817                 this.selectedIndex = -1;
31818                 this.onLoad();   
31819             }
31820         }
31821     },
31822
31823     // private
31824     getParams : function(q){
31825         var p = {};
31826         //p[this.queryParam] = q;
31827         if(this.pageSize){
31828             p.start = 0;
31829             p.limit = this.pageSize;
31830         }
31831         return p;
31832     },
31833
31834     /**
31835      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
31836      */
31837     collapse : function(){
31838         
31839     },
31840
31841     // private
31842     collapseIf : function(e){
31843         
31844     },
31845
31846     /**
31847      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
31848      */
31849     expand : function(){
31850         
31851     } ,
31852
31853     // private
31854      
31855
31856     /** 
31857     * @cfg {Boolean} grow 
31858     * @hide 
31859     */
31860     /** 
31861     * @cfg {Number} growMin 
31862     * @hide 
31863     */
31864     /** 
31865     * @cfg {Number} growMax 
31866     * @hide 
31867     */
31868     /**
31869      * @hide
31870      * @method autoSize
31871      */
31872     
31873     setWidth : function()
31874     {
31875         
31876     },
31877     getResizeEl : function(){
31878         return this.el;
31879     }
31880 });//<script type="text/javasscript">
31881  
31882
31883 /**
31884  * @class Roo.DDView
31885  * A DnD enabled version of Roo.View.
31886  * @param {Element/String} container The Element in which to create the View.
31887  * @param {String} tpl The template string used to create the markup for each element of the View
31888  * @param {Object} config The configuration properties. These include all the config options of
31889  * {@link Roo.View} plus some specific to this class.<br>
31890  * <p>
31891  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
31892  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
31893  * <p>
31894  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
31895 .x-view-drag-insert-above {
31896         border-top:1px dotted #3366cc;
31897 }
31898 .x-view-drag-insert-below {
31899         border-bottom:1px dotted #3366cc;
31900 }
31901 </code></pre>
31902  * 
31903  */
31904  
31905 Roo.DDView = function(container, tpl, config) {
31906     Roo.DDView.superclass.constructor.apply(this, arguments);
31907     this.getEl().setStyle("outline", "0px none");
31908     this.getEl().unselectable();
31909     if (this.dragGroup) {
31910                 this.setDraggable(this.dragGroup.split(","));
31911     }
31912     if (this.dropGroup) {
31913                 this.setDroppable(this.dropGroup.split(","));
31914     }
31915     if (this.deletable) {
31916         this.setDeletable();
31917     }
31918     this.isDirtyFlag = false;
31919         this.addEvents({
31920                 "drop" : true
31921         });
31922 };
31923
31924 Roo.extend(Roo.DDView, Roo.View, {
31925 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
31926 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
31927 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
31928 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
31929
31930         isFormField: true,
31931
31932         reset: Roo.emptyFn,
31933         
31934         clearInvalid: Roo.form.Field.prototype.clearInvalid,
31935
31936         validate: function() {
31937                 return true;
31938         },
31939         
31940         destroy: function() {
31941                 this.purgeListeners();
31942                 this.getEl.removeAllListeners();
31943                 this.getEl().remove();
31944                 if (this.dragZone) {
31945                         if (this.dragZone.destroy) {
31946                                 this.dragZone.destroy();
31947                         }
31948                 }
31949                 if (this.dropZone) {
31950                         if (this.dropZone.destroy) {
31951                                 this.dropZone.destroy();
31952                         }
31953                 }
31954         },
31955
31956 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
31957         getName: function() {
31958                 return this.name;
31959         },
31960
31961 /**     Loads the View from a JSON string representing the Records to put into the Store. */
31962         setValue: function(v) {
31963                 if (!this.store) {
31964                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
31965                 }
31966                 var data = {};
31967                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
31968                 this.store.proxy = new Roo.data.MemoryProxy(data);
31969                 this.store.load();
31970         },
31971
31972 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
31973         getValue: function() {
31974                 var result = '(';
31975                 this.store.each(function(rec) {
31976                         result += rec.id + ',';
31977                 });
31978                 return result.substr(0, result.length - 1) + ')';
31979         },
31980         
31981         getIds: function() {
31982                 var i = 0, result = new Array(this.store.getCount());
31983                 this.store.each(function(rec) {
31984                         result[i++] = rec.id;
31985                 });
31986                 return result;
31987         },
31988         
31989         isDirty: function() {
31990                 return this.isDirtyFlag;
31991         },
31992
31993 /**
31994  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
31995  *      whole Element becomes the target, and this causes the drop gesture to append.
31996  */
31997     getTargetFromEvent : function(e) {
31998                 var target = e.getTarget();
31999                 while ((target !== null) && (target.parentNode != this.el.dom)) {
32000                 target = target.parentNode;
32001                 }
32002                 if (!target) {
32003                         target = this.el.dom.lastChild || this.el.dom;
32004                 }
32005                 return target;
32006     },
32007
32008 /**
32009  *      Create the drag data which consists of an object which has the property "ddel" as
32010  *      the drag proxy element. 
32011  */
32012     getDragData : function(e) {
32013         var target = this.findItemFromChild(e.getTarget());
32014                 if(target) {
32015                         this.handleSelection(e);
32016                         var selNodes = this.getSelectedNodes();
32017             var dragData = {
32018                 source: this,
32019                 copy: this.copy || (this.allowCopy && e.ctrlKey),
32020                 nodes: selNodes,
32021                 records: []
32022                         };
32023                         var selectedIndices = this.getSelectedIndexes();
32024                         for (var i = 0; i < selectedIndices.length; i++) {
32025                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
32026                         }
32027                         if (selNodes.length == 1) {
32028                                 dragData.ddel = target.cloneNode(true); // the div element
32029                         } else {
32030                                 var div = document.createElement('div'); // create the multi element drag "ghost"
32031                                 div.className = 'multi-proxy';
32032                                 for (var i = 0, len = selNodes.length; i < len; i++) {
32033                                         div.appendChild(selNodes[i].cloneNode(true));
32034                                 }
32035                                 dragData.ddel = div;
32036                         }
32037             //console.log(dragData)
32038             //console.log(dragData.ddel.innerHTML)
32039                         return dragData;
32040                 }
32041         //console.log('nodragData')
32042                 return false;
32043     },
32044     
32045 /**     Specify to which ddGroup items in this DDView may be dragged. */
32046     setDraggable: function(ddGroup) {
32047         if (ddGroup instanceof Array) {
32048                 Roo.each(ddGroup, this.setDraggable, this);
32049                 return;
32050         }
32051         if (this.dragZone) {
32052                 this.dragZone.addToGroup(ddGroup);
32053         } else {
32054                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
32055                                 containerScroll: true,
32056                                 ddGroup: ddGroup 
32057
32058                         });
32059 //                      Draggability implies selection. DragZone's mousedown selects the element.
32060                         if (!this.multiSelect) { this.singleSelect = true; }
32061
32062 //                      Wire the DragZone's handlers up to methods in *this*
32063                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
32064                 }
32065     },
32066
32067 /**     Specify from which ddGroup this DDView accepts drops. */
32068     setDroppable: function(ddGroup) {
32069         if (ddGroup instanceof Array) {
32070                 Roo.each(ddGroup, this.setDroppable, this);
32071                 return;
32072         }
32073         if (this.dropZone) {
32074                 this.dropZone.addToGroup(ddGroup);
32075         } else {
32076                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
32077                                 containerScroll: true,
32078                                 ddGroup: ddGroup
32079                         });
32080
32081 //                      Wire the DropZone's handlers up to methods in *this*
32082                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
32083                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
32084                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
32085                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
32086                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
32087                 }
32088     },
32089
32090 /**     Decide whether to drop above or below a View node. */
32091     getDropPoint : function(e, n, dd){
32092         if (n == this.el.dom) { return "above"; }
32093                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
32094                 var c = t + (b - t) / 2;
32095                 var y = Roo.lib.Event.getPageY(e);
32096                 if(y <= c) {
32097                         return "above";
32098                 }else{
32099                         return "below";
32100                 }
32101     },
32102
32103     onNodeEnter : function(n, dd, e, data){
32104                 return false;
32105     },
32106     
32107     onNodeOver : function(n, dd, e, data){
32108                 var pt = this.getDropPoint(e, n, dd);
32109                 // set the insert point style on the target node
32110                 var dragElClass = this.dropNotAllowed;
32111                 if (pt) {
32112                         var targetElClass;
32113                         if (pt == "above"){
32114                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
32115                                 targetElClass = "x-view-drag-insert-above";
32116                         } else {
32117                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
32118                                 targetElClass = "x-view-drag-insert-below";
32119                         }
32120                         if (this.lastInsertClass != targetElClass){
32121                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
32122                                 this.lastInsertClass = targetElClass;
32123                         }
32124                 }
32125                 return dragElClass;
32126         },
32127
32128     onNodeOut : function(n, dd, e, data){
32129                 this.removeDropIndicators(n);
32130     },
32131
32132     onNodeDrop : function(n, dd, e, data){
32133         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
32134                 return false;
32135         }
32136         var pt = this.getDropPoint(e, n, dd);
32137                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
32138                 if (pt == "below") { insertAt++; }
32139                 for (var i = 0; i < data.records.length; i++) {
32140                         var r = data.records[i];
32141                         var dup = this.store.getById(r.id);
32142                         if (dup && (dd != this.dragZone)) {
32143                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
32144                         } else {
32145                                 if (data.copy) {
32146                                         this.store.insert(insertAt++, r.copy());
32147                                 } else {
32148                                         data.source.isDirtyFlag = true;
32149                                         r.store.remove(r);
32150                                         this.store.insert(insertAt++, r);
32151                                 }
32152                                 this.isDirtyFlag = true;
32153                         }
32154                 }
32155                 this.dragZone.cachedTarget = null;
32156                 return true;
32157     },
32158
32159     removeDropIndicators : function(n){
32160                 if(n){
32161                         Roo.fly(n).removeClass([
32162                                 "x-view-drag-insert-above",
32163                                 "x-view-drag-insert-below"]);
32164                         this.lastInsertClass = "_noclass";
32165                 }
32166     },
32167
32168 /**
32169  *      Utility method. Add a delete option to the DDView's context menu.
32170  *      @param {String} imageUrl The URL of the "delete" icon image.
32171  */
32172         setDeletable: function(imageUrl) {
32173                 if (!this.singleSelect && !this.multiSelect) {
32174                         this.singleSelect = true;
32175                 }
32176                 var c = this.getContextMenu();
32177                 this.contextMenu.on("itemclick", function(item) {
32178                         switch (item.id) {
32179                                 case "delete":
32180                                         this.remove(this.getSelectedIndexes());
32181                                         break;
32182                         }
32183                 }, this);
32184                 this.contextMenu.add({
32185                         icon: imageUrl,
32186                         id: "delete",
32187                         text: 'Delete'
32188                 });
32189         },
32190         
32191 /**     Return the context menu for this DDView. */
32192         getContextMenu: function() {
32193                 if (!this.contextMenu) {
32194 //                      Create the View's context menu
32195                         this.contextMenu = new Roo.menu.Menu({
32196                                 id: this.id + "-contextmenu"
32197                         });
32198                         this.el.on("contextmenu", this.showContextMenu, this);
32199                 }
32200                 return this.contextMenu;
32201         },
32202         
32203         disableContextMenu: function() {
32204                 if (this.contextMenu) {
32205                         this.el.un("contextmenu", this.showContextMenu, this);
32206                 }
32207         },
32208
32209         showContextMenu: function(e, item) {
32210         item = this.findItemFromChild(e.getTarget());
32211                 if (item) {
32212                         e.stopEvent();
32213                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
32214                         this.contextMenu.showAt(e.getXY());
32215             }
32216     },
32217
32218 /**
32219  *      Remove {@link Roo.data.Record}s at the specified indices.
32220  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
32221  */
32222     remove: function(selectedIndices) {
32223                 selectedIndices = [].concat(selectedIndices);
32224                 for (var i = 0; i < selectedIndices.length; i++) {
32225                         var rec = this.store.getAt(selectedIndices[i]);
32226                         this.store.remove(rec);
32227                 }
32228     },
32229
32230 /**
32231  *      Double click fires the event, but also, if this is draggable, and there is only one other
32232  *      related DropZone, it transfers the selected node.
32233  */
32234     onDblClick : function(e){
32235         var item = this.findItemFromChild(e.getTarget());
32236         if(item){
32237             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
32238                 return false;
32239             }
32240             if (this.dragGroup) {
32241                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
32242                     while (targets.indexOf(this.dropZone) > -1) {
32243                             targets.remove(this.dropZone);
32244                                 }
32245                     if (targets.length == 1) {
32246                                         this.dragZone.cachedTarget = null;
32247                         var el = Roo.get(targets[0].getEl());
32248                         var box = el.getBox(true);
32249                         targets[0].onNodeDrop(el.dom, {
32250                                 target: el.dom,
32251                                 xy: [box.x, box.y + box.height - 1]
32252                         }, null, this.getDragData(e));
32253                     }
32254                 }
32255         }
32256     },
32257     
32258     handleSelection: function(e) {
32259                 this.dragZone.cachedTarget = null;
32260         var item = this.findItemFromChild(e.getTarget());
32261         if (!item) {
32262                 this.clearSelections(true);
32263                 return;
32264         }
32265                 if (item && (this.multiSelect || this.singleSelect)){
32266                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
32267                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
32268                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
32269                                 this.unselect(item);
32270                         } else {
32271                                 this.select(item, this.multiSelect && e.ctrlKey);
32272                                 this.lastSelection = item;
32273                         }
32274                 }
32275     },
32276
32277     onItemClick : function(item, index, e){
32278                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
32279                         return false;
32280                 }
32281                 return true;
32282     },
32283
32284     unselect : function(nodeInfo, suppressEvent){
32285                 var node = this.getNode(nodeInfo);
32286                 if(node && this.isSelected(node)){
32287                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
32288                                 Roo.fly(node).removeClass(this.selectedClass);
32289                                 this.selections.remove(node);
32290                                 if(!suppressEvent){
32291                                         this.fireEvent("selectionchange", this, this.selections);
32292                                 }
32293                         }
32294                 }
32295     }
32296 });
32297 /*
32298  * Based on:
32299  * Ext JS Library 1.1.1
32300  * Copyright(c) 2006-2007, Ext JS, LLC.
32301  *
32302  * Originally Released Under LGPL - original licence link has changed is not relivant.
32303  *
32304  * Fork - LGPL
32305  * <script type="text/javascript">
32306  */
32307  
32308 /**
32309  * @class Roo.LayoutManager
32310  * @extends Roo.util.Observable
32311  * Base class for layout managers.
32312  */
32313 Roo.LayoutManager = function(container, config){
32314     Roo.LayoutManager.superclass.constructor.call(this);
32315     this.el = Roo.get(container);
32316     // ie scrollbar fix
32317     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32318         document.body.scroll = "no";
32319     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32320         this.el.position('relative');
32321     }
32322     this.id = this.el.id;
32323     this.el.addClass("x-layout-container");
32324     /** false to disable window resize monitoring @type Boolean */
32325     this.monitorWindowResize = true;
32326     this.regions = {};
32327     this.addEvents({
32328         /**
32329          * @event layout
32330          * Fires when a layout is performed. 
32331          * @param {Roo.LayoutManager} this
32332          */
32333         "layout" : true,
32334         /**
32335          * @event regionresized
32336          * Fires when the user resizes a region. 
32337          * @param {Roo.LayoutRegion} region The resized region
32338          * @param {Number} newSize The new size (width for east/west, height for north/south)
32339          */
32340         "regionresized" : true,
32341         /**
32342          * @event regioncollapsed
32343          * Fires when a region is collapsed. 
32344          * @param {Roo.LayoutRegion} region The collapsed region
32345          */
32346         "regioncollapsed" : true,
32347         /**
32348          * @event regionexpanded
32349          * Fires when a region is expanded.  
32350          * @param {Roo.LayoutRegion} region The expanded region
32351          */
32352         "regionexpanded" : true
32353     });
32354     this.updating = false;
32355     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32356 };
32357
32358 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
32359     /**
32360      * Returns true if this layout is currently being updated
32361      * @return {Boolean}
32362      */
32363     isUpdating : function(){
32364         return this.updating; 
32365     },
32366     
32367     /**
32368      * Suspend the LayoutManager from doing auto-layouts while
32369      * making multiple add or remove calls
32370      */
32371     beginUpdate : function(){
32372         this.updating = true;    
32373     },
32374     
32375     /**
32376      * Restore auto-layouts and optionally disable the manager from performing a layout
32377      * @param {Boolean} noLayout true to disable a layout update 
32378      */
32379     endUpdate : function(noLayout){
32380         this.updating = false;
32381         if(!noLayout){
32382             this.layout();
32383         }    
32384     },
32385     
32386     layout: function(){
32387         
32388     },
32389     
32390     onRegionResized : function(region, newSize){
32391         this.fireEvent("regionresized", region, newSize);
32392         this.layout();
32393     },
32394     
32395     onRegionCollapsed : function(region){
32396         this.fireEvent("regioncollapsed", region);
32397     },
32398     
32399     onRegionExpanded : function(region){
32400         this.fireEvent("regionexpanded", region);
32401     },
32402         
32403     /**
32404      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32405      * performs box-model adjustments.
32406      * @return {Object} The size as an object {width: (the width), height: (the height)}
32407      */
32408     getViewSize : function(){
32409         var size;
32410         if(this.el.dom != document.body){
32411             size = this.el.getSize();
32412         }else{
32413             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32414         }
32415         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32416         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32417         return size;
32418     },
32419     
32420     /**
32421      * Returns the Element this layout is bound to.
32422      * @return {Roo.Element}
32423      */
32424     getEl : function(){
32425         return this.el;
32426     },
32427     
32428     /**
32429      * Returns the specified region.
32430      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32431      * @return {Roo.LayoutRegion}
32432      */
32433     getRegion : function(target){
32434         return this.regions[target.toLowerCase()];
32435     },
32436     
32437     onWindowResize : function(){
32438         if(this.monitorWindowResize){
32439             this.layout();
32440         }
32441     }
32442 });/*
32443  * Based on:
32444  * Ext JS Library 1.1.1
32445  * Copyright(c) 2006-2007, Ext JS, LLC.
32446  *
32447  * Originally Released Under LGPL - original licence link has changed is not relivant.
32448  *
32449  * Fork - LGPL
32450  * <script type="text/javascript">
32451  */
32452 /**
32453  * @class Roo.BorderLayout
32454  * @extends Roo.LayoutManager
32455  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32456  * please see: <br><br>
32457  * <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>
32458  * <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>
32459  * Example:
32460  <pre><code>
32461  var layout = new Roo.BorderLayout(document.body, {
32462     north: {
32463         initialSize: 25,
32464         titlebar: false
32465     },
32466     west: {
32467         split:true,
32468         initialSize: 200,
32469         minSize: 175,
32470         maxSize: 400,
32471         titlebar: true,
32472         collapsible: true
32473     },
32474     east: {
32475         split:true,
32476         initialSize: 202,
32477         minSize: 175,
32478         maxSize: 400,
32479         titlebar: true,
32480         collapsible: true
32481     },
32482     south: {
32483         split:true,
32484         initialSize: 100,
32485         minSize: 100,
32486         maxSize: 200,
32487         titlebar: true,
32488         collapsible: true
32489     },
32490     center: {
32491         titlebar: true,
32492         autoScroll:true,
32493         resizeTabs: true,
32494         minTabWidth: 50,
32495         preferredTabWidth: 150
32496     }
32497 });
32498
32499 // shorthand
32500 var CP = Roo.ContentPanel;
32501
32502 layout.beginUpdate();
32503 layout.add("north", new CP("north", "North"));
32504 layout.add("south", new CP("south", {title: "South", closable: true}));
32505 layout.add("west", new CP("west", {title: "West"}));
32506 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
32507 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
32508 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
32509 layout.getRegion("center").showPanel("center1");
32510 layout.endUpdate();
32511 </code></pre>
32512
32513 <b>The container the layout is rendered into can be either the body element or any other element.
32514 If it is not the body element, the container needs to either be an absolute positioned element,
32515 or you will need to add "position:relative" to the css of the container.  You will also need to specify
32516 the container size if it is not the body element.</b>
32517
32518 * @constructor
32519 * Create a new BorderLayout
32520 * @param {String/HTMLElement/Element} container The container this layout is bound to
32521 * @param {Object} config Configuration options
32522  */
32523 Roo.BorderLayout = function(container, config){
32524     config = config || {};
32525     Roo.BorderLayout.superclass.constructor.call(this, container, config);
32526     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
32527     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
32528         var target = this.factory.validRegions[i];
32529         if(config[target]){
32530             this.addRegion(target, config[target]);
32531         }
32532     }
32533 };
32534
32535 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
32536     /**
32537      * Creates and adds a new region if it doesn't already exist.
32538      * @param {String} target The target region key (north, south, east, west or center).
32539      * @param {Object} config The regions config object
32540      * @return {BorderLayoutRegion} The new region
32541      */
32542     addRegion : function(target, config){
32543         if(!this.regions[target]){
32544             var r = this.factory.create(target, this, config);
32545             this.bindRegion(target, r);
32546         }
32547         return this.regions[target];
32548     },
32549
32550     // private (kinda)
32551     bindRegion : function(name, r){
32552         this.regions[name] = r;
32553         r.on("visibilitychange", this.layout, this);
32554         r.on("paneladded", this.layout, this);
32555         r.on("panelremoved", this.layout, this);
32556         r.on("invalidated", this.layout, this);
32557         r.on("resized", this.onRegionResized, this);
32558         r.on("collapsed", this.onRegionCollapsed, this);
32559         r.on("expanded", this.onRegionExpanded, this);
32560     },
32561
32562     /**
32563      * Performs a layout update.
32564      */
32565     layout : function(){
32566         if(this.updating) return;
32567         var size = this.getViewSize();
32568         var w = size.width;
32569         var h = size.height;
32570         var centerW = w;
32571         var centerH = h;
32572         var centerY = 0;
32573         var centerX = 0;
32574         //var x = 0, y = 0;
32575
32576         var rs = this.regions;
32577         var north = rs["north"];
32578         var south = rs["south"]; 
32579         var west = rs["west"];
32580         var east = rs["east"];
32581         var center = rs["center"];
32582         //if(this.hideOnLayout){ // not supported anymore
32583             //c.el.setStyle("display", "none");
32584         //}
32585         if(north && north.isVisible()){
32586             var b = north.getBox();
32587             var m = north.getMargins();
32588             b.width = w - (m.left+m.right);
32589             b.x = m.left;
32590             b.y = m.top;
32591             centerY = b.height + b.y + m.bottom;
32592             centerH -= centerY;
32593             north.updateBox(this.safeBox(b));
32594         }
32595         if(south && south.isVisible()){
32596             var b = south.getBox();
32597             var m = south.getMargins();
32598             b.width = w - (m.left+m.right);
32599             b.x = m.left;
32600             var totalHeight = (b.height + m.top + m.bottom);
32601             b.y = h - totalHeight + m.top;
32602             centerH -= totalHeight;
32603             south.updateBox(this.safeBox(b));
32604         }
32605         if(west && west.isVisible()){
32606             var b = west.getBox();
32607             var m = west.getMargins();
32608             b.height = centerH - (m.top+m.bottom);
32609             b.x = m.left;
32610             b.y = centerY + m.top;
32611             var totalWidth = (b.width + m.left + m.right);
32612             centerX += totalWidth;
32613             centerW -= totalWidth;
32614             west.updateBox(this.safeBox(b));
32615         }
32616         if(east && east.isVisible()){
32617             var b = east.getBox();
32618             var m = east.getMargins();
32619             b.height = centerH - (m.top+m.bottom);
32620             var totalWidth = (b.width + m.left + m.right);
32621             b.x = w - totalWidth + m.left;
32622             b.y = centerY + m.top;
32623             centerW -= totalWidth;
32624             east.updateBox(this.safeBox(b));
32625         }
32626         if(center){
32627             var m = center.getMargins();
32628             var centerBox = {
32629                 x: centerX + m.left,
32630                 y: centerY + m.top,
32631                 width: centerW - (m.left+m.right),
32632                 height: centerH - (m.top+m.bottom)
32633             };
32634             //if(this.hideOnLayout){
32635                 //center.el.setStyle("display", "block");
32636             //}
32637             center.updateBox(this.safeBox(centerBox));
32638         }
32639         this.el.repaint();
32640         this.fireEvent("layout", this);
32641     },
32642
32643     // private
32644     safeBox : function(box){
32645         box.width = Math.max(0, box.width);
32646         box.height = Math.max(0, box.height);
32647         return box;
32648     },
32649
32650     /**
32651      * Adds a ContentPanel (or subclass) to this layout.
32652      * @param {String} target The target region key (north, south, east, west or center).
32653      * @param {Roo.ContentPanel} panel The panel to add
32654      * @return {Roo.ContentPanel} The added panel
32655      */
32656     add : function(target, panel){
32657          
32658         target = target.toLowerCase();
32659         return this.regions[target].add(panel);
32660     },
32661
32662     /**
32663      * Remove a ContentPanel (or subclass) to this layout.
32664      * @param {String} target The target region key (north, south, east, west or center).
32665      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
32666      * @return {Roo.ContentPanel} The removed panel
32667      */
32668     remove : function(target, panel){
32669         target = target.toLowerCase();
32670         return this.regions[target].remove(panel);
32671     },
32672
32673     /**
32674      * Searches all regions for a panel with the specified id
32675      * @param {String} panelId
32676      * @return {Roo.ContentPanel} The panel or null if it wasn't found
32677      */
32678     findPanel : function(panelId){
32679         var rs = this.regions;
32680         for(var target in rs){
32681             if(typeof rs[target] != "function"){
32682                 var p = rs[target].getPanel(panelId);
32683                 if(p){
32684                     return p;
32685                 }
32686             }
32687         }
32688         return null;
32689     },
32690
32691     /**
32692      * Searches all regions for a panel with the specified id and activates (shows) it.
32693      * @param {String/ContentPanel} panelId The panels id or the panel itself
32694      * @return {Roo.ContentPanel} The shown panel or null
32695      */
32696     showPanel : function(panelId) {
32697       var rs = this.regions;
32698       for(var target in rs){
32699          var r = rs[target];
32700          if(typeof r != "function"){
32701             if(r.hasPanel(panelId)){
32702                return r.showPanel(panelId);
32703             }
32704          }
32705       }
32706       return null;
32707    },
32708
32709    /**
32710      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32711      * @param {Roo.state.Provider} provider (optional) An alternate state provider
32712      */
32713     restoreState : function(provider){
32714         if(!provider){
32715             provider = Roo.state.Manager;
32716         }
32717         var sm = new Roo.LayoutStateManager();
32718         sm.init(this, provider);
32719     },
32720
32721     /**
32722      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
32723      * object should contain properties for each region to add ContentPanels to, and each property's value should be
32724      * a valid ContentPanel config object.  Example:
32725      * <pre><code>
32726 // Create the main layout
32727 var layout = new Roo.BorderLayout('main-ct', {
32728     west: {
32729         split:true,
32730         minSize: 175,
32731         titlebar: true
32732     },
32733     center: {
32734         title:'Components'
32735     }
32736 }, 'main-ct');
32737
32738 // Create and add multiple ContentPanels at once via configs
32739 layout.batchAdd({
32740    west: {
32741        id: 'source-files',
32742        autoCreate:true,
32743        title:'Ext Source Files',
32744        autoScroll:true,
32745        fitToFrame:true
32746    },
32747    center : {
32748        el: cview,
32749        autoScroll:true,
32750        fitToFrame:true,
32751        toolbar: tb,
32752        resizeEl:'cbody'
32753    }
32754 });
32755 </code></pre>
32756      * @param {Object} regions An object containing ContentPanel configs by region name
32757      */
32758     batchAdd : function(regions){
32759         this.beginUpdate();
32760         for(var rname in regions){
32761             var lr = this.regions[rname];
32762             if(lr){
32763                 this.addTypedPanels(lr, regions[rname]);
32764             }
32765         }
32766         this.endUpdate();
32767     },
32768
32769     // private
32770     addTypedPanels : function(lr, ps){
32771         if(typeof ps == 'string'){
32772             lr.add(new Roo.ContentPanel(ps));
32773         }
32774         else if(ps instanceof Array){
32775             for(var i =0, len = ps.length; i < len; i++){
32776                 this.addTypedPanels(lr, ps[i]);
32777             }
32778         }
32779         else if(!ps.events){ // raw config?
32780             var el = ps.el;
32781             delete ps.el; // prevent conflict
32782             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
32783         }
32784         else {  // panel object assumed!
32785             lr.add(ps);
32786         }
32787     },
32788     /**
32789      * Adds a xtype elements to the layout.
32790      * <pre><code>
32791
32792 layout.addxtype({
32793        xtype : 'ContentPanel',
32794        region: 'west',
32795        items: [ .... ]
32796    }
32797 );
32798
32799 layout.addxtype({
32800         xtype : 'NestedLayoutPanel',
32801         region: 'west',
32802         layout: {
32803            center: { },
32804            west: { }   
32805         },
32806         items : [ ... list of content panels or nested layout panels.. ]
32807    }
32808 );
32809 </code></pre>
32810      * @param {Object} cfg Xtype definition of item to add.
32811      */
32812     addxtype : function(cfg)
32813     {
32814         // basically accepts a pannel...
32815         // can accept a layout region..!?!?
32816         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32817         
32818         if (!cfg.xtype.match(/Panel$/)) {
32819             return false;
32820         }
32821         var ret = false;
32822         
32823         if (typeof(cfg.region) == 'undefined') {
32824             Roo.log("Failed to add Panel, region was not set");
32825             Roo.log(cfg);
32826             return false;
32827         }
32828         var region = cfg.region;
32829         delete cfg.region;
32830         
32831           
32832         var xitems = [];
32833         if (cfg.items) {
32834             xitems = cfg.items;
32835             delete cfg.items;
32836         }
32837         var nb = false;
32838         
32839         switch(cfg.xtype) 
32840         {
32841             case 'ContentPanel':  // ContentPanel (el, cfg)
32842             case 'ScrollPanel':  // ContentPanel (el, cfg)
32843             case 'ViewPanel': 
32844                 if(cfg.autoCreate) {
32845                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32846                 } else {
32847                     var el = this.el.createChild();
32848                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32849                 }
32850                 
32851                 this.add(region, ret);
32852                 break;
32853             
32854             
32855             case 'TreePanel': // our new panel!
32856                 cfg.el = this.el.createChild();
32857                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32858                 this.add(region, ret);
32859                 break;
32860             
32861             case 'NestedLayoutPanel': 
32862                 // create a new Layout (which is  a Border Layout...
32863                 var el = this.el.createChild();
32864                 var clayout = cfg.layout;
32865                 delete cfg.layout;
32866                 clayout.items   = clayout.items  || [];
32867                 // replace this exitems with the clayout ones..
32868                 xitems = clayout.items;
32869                  
32870                 
32871                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32872                     cfg.background = false;
32873                 }
32874                 var layout = new Roo.BorderLayout(el, clayout);
32875                 
32876                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
32877                 //console.log('adding nested layout panel '  + cfg.toSource());
32878                 this.add(region, ret);
32879                 nb = {}; /// find first...
32880                 break;
32881                 
32882             case 'GridPanel': 
32883             
32884                 // needs grid and region
32885                 
32886                 //var el = this.getRegion(region).el.createChild();
32887                 var el = this.el.createChild();
32888                 // create the grid first...
32889                 
32890                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
32891                 delete cfg.grid;
32892                 if (region == 'center' && this.active ) {
32893                     cfg.background = false;
32894                 }
32895                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
32896                 
32897                 this.add(region, ret);
32898                 if (cfg.background) {
32899                     ret.on('activate', function(gp) {
32900                         if (!gp.grid.rendered) {
32901                             gp.grid.render();
32902                         }
32903                     });
32904                 } else {
32905                     grid.render();
32906                 }
32907                 break;
32908            
32909            
32910            
32911                 
32912                 
32913                 
32914             default:
32915                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
32916                     
32917                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32918                     this.add(region, ret);
32919                 } else {
32920                 
32921                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
32922                     return null;
32923                 }
32924                 
32925              // GridPanel (grid, cfg)
32926             
32927         }
32928         this.beginUpdate();
32929         // add children..
32930         var region = '';
32931         var abn = {};
32932         Roo.each(xitems, function(i)  {
32933             region = nb && i.region ? i.region : false;
32934             
32935             var add = ret.addxtype(i);
32936            
32937             if (region) {
32938                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32939                 if (!i.background) {
32940                     abn[region] = nb[region] ;
32941                 }
32942             }
32943             
32944         });
32945         this.endUpdate();
32946
32947         // make the last non-background panel active..
32948         //if (nb) { Roo.log(abn); }
32949         if (nb) {
32950             
32951             for(var r in abn) {
32952                 region = this.getRegion(r);
32953                 if (region) {
32954                     // tried using nb[r], but it does not work..
32955                      
32956                     region.showPanel(abn[r]);
32957                    
32958                 }
32959             }
32960         }
32961         return ret;
32962         
32963     }
32964 });
32965
32966 /**
32967  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
32968  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
32969  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
32970  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
32971  * <pre><code>
32972 // shorthand
32973 var CP = Roo.ContentPanel;
32974
32975 var layout = Roo.BorderLayout.create({
32976     north: {
32977         initialSize: 25,
32978         titlebar: false,
32979         panels: [new CP("north", "North")]
32980     },
32981     west: {
32982         split:true,
32983         initialSize: 200,
32984         minSize: 175,
32985         maxSize: 400,
32986         titlebar: true,
32987         collapsible: true,
32988         panels: [new CP("west", {title: "West"})]
32989     },
32990     east: {
32991         split:true,
32992         initialSize: 202,
32993         minSize: 175,
32994         maxSize: 400,
32995         titlebar: true,
32996         collapsible: true,
32997         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
32998     },
32999     south: {
33000         split:true,
33001         initialSize: 100,
33002         minSize: 100,
33003         maxSize: 200,
33004         titlebar: true,
33005         collapsible: true,
33006         panels: [new CP("south", {title: "South", closable: true})]
33007     },
33008     center: {
33009         titlebar: true,
33010         autoScroll:true,
33011         resizeTabs: true,
33012         minTabWidth: 50,
33013         preferredTabWidth: 150,
33014         panels: [
33015             new CP("center1", {title: "Close Me", closable: true}),
33016             new CP("center2", {title: "Center Panel", closable: false})
33017         ]
33018     }
33019 }, document.body);
33020
33021 layout.getRegion("center").showPanel("center1");
33022 </code></pre>
33023  * @param config
33024  * @param targetEl
33025  */
33026 Roo.BorderLayout.create = function(config, targetEl){
33027     var layout = new Roo.BorderLayout(targetEl || document.body, config);
33028     layout.beginUpdate();
33029     var regions = Roo.BorderLayout.RegionFactory.validRegions;
33030     for(var j = 0, jlen = regions.length; j < jlen; j++){
33031         var lr = regions[j];
33032         if(layout.regions[lr] && config[lr].panels){
33033             var r = layout.regions[lr];
33034             var ps = config[lr].panels;
33035             layout.addTypedPanels(r, ps);
33036         }
33037     }
33038     layout.endUpdate();
33039     return layout;
33040 };
33041
33042 // private
33043 Roo.BorderLayout.RegionFactory = {
33044     // private
33045     validRegions : ["north","south","east","west","center"],
33046
33047     // private
33048     create : function(target, mgr, config){
33049         target = target.toLowerCase();
33050         if(config.lightweight || config.basic){
33051             return new Roo.BasicLayoutRegion(mgr, config, target);
33052         }
33053         switch(target){
33054             case "north":
33055                 return new Roo.NorthLayoutRegion(mgr, config);
33056             case "south":
33057                 return new Roo.SouthLayoutRegion(mgr, config);
33058             case "east":
33059                 return new Roo.EastLayoutRegion(mgr, config);
33060             case "west":
33061                 return new Roo.WestLayoutRegion(mgr, config);
33062             case "center":
33063                 return new Roo.CenterLayoutRegion(mgr, config);
33064         }
33065         throw 'Layout region "'+target+'" not supported.';
33066     }
33067 };/*
33068  * Based on:
33069  * Ext JS Library 1.1.1
33070  * Copyright(c) 2006-2007, Ext JS, LLC.
33071  *
33072  * Originally Released Under LGPL - original licence link has changed is not relivant.
33073  *
33074  * Fork - LGPL
33075  * <script type="text/javascript">
33076  */
33077  
33078 /**
33079  * @class Roo.BasicLayoutRegion
33080  * @extends Roo.util.Observable
33081  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
33082  * and does not have a titlebar, tabs or any other features. All it does is size and position 
33083  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
33084  */
33085 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
33086     this.mgr = mgr;
33087     this.position  = pos;
33088     this.events = {
33089         /**
33090          * @scope Roo.BasicLayoutRegion
33091          */
33092         
33093         /**
33094          * @event beforeremove
33095          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
33096          * @param {Roo.LayoutRegion} this
33097          * @param {Roo.ContentPanel} panel The panel
33098          * @param {Object} e The cancel event object
33099          */
33100         "beforeremove" : true,
33101         /**
33102          * @event invalidated
33103          * Fires when the layout for this region is changed.
33104          * @param {Roo.LayoutRegion} this
33105          */
33106         "invalidated" : true,
33107         /**
33108          * @event visibilitychange
33109          * Fires when this region is shown or hidden 
33110          * @param {Roo.LayoutRegion} this
33111          * @param {Boolean} visibility true or false
33112          */
33113         "visibilitychange" : true,
33114         /**
33115          * @event paneladded
33116          * Fires when a panel is added. 
33117          * @param {Roo.LayoutRegion} this
33118          * @param {Roo.ContentPanel} panel The panel
33119          */
33120         "paneladded" : true,
33121         /**
33122          * @event panelremoved
33123          * Fires when a panel is removed. 
33124          * @param {Roo.LayoutRegion} this
33125          * @param {Roo.ContentPanel} panel The panel
33126          */
33127         "panelremoved" : true,
33128         /**
33129          * @event collapsed
33130          * Fires when this region is collapsed.
33131          * @param {Roo.LayoutRegion} this
33132          */
33133         "collapsed" : true,
33134         /**
33135          * @event expanded
33136          * Fires when this region is expanded.
33137          * @param {Roo.LayoutRegion} this
33138          */
33139         "expanded" : true,
33140         /**
33141          * @event slideshow
33142          * Fires when this region is slid into view.
33143          * @param {Roo.LayoutRegion} this
33144          */
33145         "slideshow" : true,
33146         /**
33147          * @event slidehide
33148          * Fires when this region slides out of view. 
33149          * @param {Roo.LayoutRegion} this
33150          */
33151         "slidehide" : true,
33152         /**
33153          * @event panelactivated
33154          * Fires when a panel is activated. 
33155          * @param {Roo.LayoutRegion} this
33156          * @param {Roo.ContentPanel} panel The activated panel
33157          */
33158         "panelactivated" : true,
33159         /**
33160          * @event resized
33161          * Fires when the user resizes this region. 
33162          * @param {Roo.LayoutRegion} this
33163          * @param {Number} newSize The new size (width for east/west, height for north/south)
33164          */
33165         "resized" : true
33166     };
33167     /** A collection of panels in this region. @type Roo.util.MixedCollection */
33168     this.panels = new Roo.util.MixedCollection();
33169     this.panels.getKey = this.getPanelId.createDelegate(this);
33170     this.box = null;
33171     this.activePanel = null;
33172     // ensure listeners are added...
33173     
33174     if (config.listeners || config.events) {
33175         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
33176             listeners : config.listeners || {},
33177             events : config.events || {}
33178         });
33179     }
33180     
33181     if(skipConfig !== true){
33182         this.applyConfig(config);
33183     }
33184 };
33185
33186 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
33187     getPanelId : function(p){
33188         return p.getId();
33189     },
33190     
33191     applyConfig : function(config){
33192         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33193         this.config = config;
33194         
33195     },
33196     
33197     /**
33198      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
33199      * the width, for horizontal (north, south) the height.
33200      * @param {Number} newSize The new width or height
33201      */
33202     resizeTo : function(newSize){
33203         var el = this.el ? this.el :
33204                  (this.activePanel ? this.activePanel.getEl() : null);
33205         if(el){
33206             switch(this.position){
33207                 case "east":
33208                 case "west":
33209                     el.setWidth(newSize);
33210                     this.fireEvent("resized", this, newSize);
33211                 break;
33212                 case "north":
33213                 case "south":
33214                     el.setHeight(newSize);
33215                     this.fireEvent("resized", this, newSize);
33216                 break;                
33217             }
33218         }
33219     },
33220     
33221     getBox : function(){
33222         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33223     },
33224     
33225     getMargins : function(){
33226         return this.margins;
33227     },
33228     
33229     updateBox : function(box){
33230         this.box = box;
33231         var el = this.activePanel.getEl();
33232         el.dom.style.left = box.x + "px";
33233         el.dom.style.top = box.y + "px";
33234         this.activePanel.setSize(box.width, box.height);
33235     },
33236     
33237     /**
33238      * Returns the container element for this region.
33239      * @return {Roo.Element}
33240      */
33241     getEl : function(){
33242         return this.activePanel;
33243     },
33244     
33245     /**
33246      * Returns true if this region is currently visible.
33247      * @return {Boolean}
33248      */
33249     isVisible : function(){
33250         return this.activePanel ? true : false;
33251     },
33252     
33253     setActivePanel : function(panel){
33254         panel = this.getPanel(panel);
33255         if(this.activePanel && this.activePanel != panel){
33256             this.activePanel.setActiveState(false);
33257             this.activePanel.getEl().setLeftTop(-10000,-10000);
33258         }
33259         this.activePanel = panel;
33260         panel.setActiveState(true);
33261         if(this.box){
33262             panel.setSize(this.box.width, this.box.height);
33263         }
33264         this.fireEvent("panelactivated", this, panel);
33265         this.fireEvent("invalidated");
33266     },
33267     
33268     /**
33269      * Show the specified panel.
33270      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33271      * @return {Roo.ContentPanel} The shown panel or null
33272      */
33273     showPanel : function(panel){
33274         if(panel = this.getPanel(panel)){
33275             this.setActivePanel(panel);
33276         }
33277         return panel;
33278     },
33279     
33280     /**
33281      * Get the active panel for this region.
33282      * @return {Roo.ContentPanel} The active panel or null
33283      */
33284     getActivePanel : function(){
33285         return this.activePanel;
33286     },
33287     
33288     /**
33289      * Add the passed ContentPanel(s)
33290      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33291      * @return {Roo.ContentPanel} The panel added (if only one was added)
33292      */
33293     add : function(panel){
33294         if(arguments.length > 1){
33295             for(var i = 0, len = arguments.length; i < len; i++) {
33296                 this.add(arguments[i]);
33297             }
33298             return null;
33299         }
33300         if(this.hasPanel(panel)){
33301             this.showPanel(panel);
33302             return panel;
33303         }
33304         var el = panel.getEl();
33305         if(el.dom.parentNode != this.mgr.el.dom){
33306             this.mgr.el.dom.appendChild(el.dom);
33307         }
33308         if(panel.setRegion){
33309             panel.setRegion(this);
33310         }
33311         this.panels.add(panel);
33312         el.setStyle("position", "absolute");
33313         if(!panel.background){
33314             this.setActivePanel(panel);
33315             if(this.config.initialSize && this.panels.getCount()==1){
33316                 this.resizeTo(this.config.initialSize);
33317             }
33318         }
33319         this.fireEvent("paneladded", this, panel);
33320         return panel;
33321     },
33322     
33323     /**
33324      * Returns true if the panel is in this region.
33325      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33326      * @return {Boolean}
33327      */
33328     hasPanel : function(panel){
33329         if(typeof panel == "object"){ // must be panel obj
33330             panel = panel.getId();
33331         }
33332         return this.getPanel(panel) ? true : false;
33333     },
33334     
33335     /**
33336      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33337      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33338      * @param {Boolean} preservePanel Overrides the config preservePanel option
33339      * @return {Roo.ContentPanel} The panel that was removed
33340      */
33341     remove : function(panel, preservePanel){
33342         panel = this.getPanel(panel);
33343         if(!panel){
33344             return null;
33345         }
33346         var e = {};
33347         this.fireEvent("beforeremove", this, panel, e);
33348         if(e.cancel === true){
33349             return null;
33350         }
33351         var panelId = panel.getId();
33352         this.panels.removeKey(panelId);
33353         return panel;
33354     },
33355     
33356     /**
33357      * Returns the panel specified or null if it's not in this region.
33358      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33359      * @return {Roo.ContentPanel}
33360      */
33361     getPanel : function(id){
33362         if(typeof id == "object"){ // must be panel obj
33363             return id;
33364         }
33365         return this.panels.get(id);
33366     },
33367     
33368     /**
33369      * Returns this regions position (north/south/east/west/center).
33370      * @return {String} 
33371      */
33372     getPosition: function(){
33373         return this.position;    
33374     }
33375 });/*
33376  * Based on:
33377  * Ext JS Library 1.1.1
33378  * Copyright(c) 2006-2007, Ext JS, LLC.
33379  *
33380  * Originally Released Under LGPL - original licence link has changed is not relivant.
33381  *
33382  * Fork - LGPL
33383  * <script type="text/javascript">
33384  */
33385  
33386 /**
33387  * @class Roo.LayoutRegion
33388  * @extends Roo.BasicLayoutRegion
33389  * This class represents a region in a layout manager.
33390  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
33391  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
33392  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
33393  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33394  * @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})
33395  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
33396  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
33397  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
33398  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
33399  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
33400  * @cfg {String}    title           The title for the region (overrides panel titles)
33401  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
33402  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33403  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
33404  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33405  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
33406  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33407  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
33408  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
33409  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
33410  * @cfg {Boolean}   showPin         True to show a pin button
33411  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
33412  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
33413  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
33414  * @cfg {Number}    width           For East/West panels
33415  * @cfg {Number}    height          For North/South panels
33416  * @cfg {Boolean}   split           To show the splitter
33417  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
33418  */
33419 Roo.LayoutRegion = function(mgr, config, pos){
33420     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
33421     var dh = Roo.DomHelper;
33422     /** This region's container element 
33423     * @type Roo.Element */
33424     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
33425     /** This region's title element 
33426     * @type Roo.Element */
33427
33428     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
33429         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
33430         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
33431     ]}, true);
33432     this.titleEl.enableDisplayMode();
33433     /** This region's title text element 
33434     * @type HTMLElement */
33435     this.titleTextEl = this.titleEl.dom.firstChild;
33436     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33437     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
33438     this.closeBtn.enableDisplayMode();
33439     this.closeBtn.on("click", this.closeClicked, this);
33440     this.closeBtn.hide();
33441
33442     this.createBody(config);
33443     this.visible = true;
33444     this.collapsed = false;
33445
33446     if(config.hideWhenEmpty){
33447         this.hide();
33448         this.on("paneladded", this.validateVisibility, this);
33449         this.on("panelremoved", this.validateVisibility, this);
33450     }
33451     this.applyConfig(config);
33452 };
33453
33454 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
33455
33456     createBody : function(){
33457         /** This region's body element 
33458         * @type Roo.Element */
33459         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
33460     },
33461
33462     applyConfig : function(c){
33463         if(c.collapsible && this.position != "center" && !this.collapsedEl){
33464             var dh = Roo.DomHelper;
33465             if(c.titlebar !== false){
33466                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
33467                 this.collapseBtn.on("click", this.collapse, this);
33468                 this.collapseBtn.enableDisplayMode();
33469
33470                 if(c.showPin === true || this.showPin){
33471                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
33472                     this.stickBtn.enableDisplayMode();
33473                     this.stickBtn.on("click", this.expand, this);
33474                     this.stickBtn.hide();
33475                 }
33476             }
33477             /** This region's collapsed element
33478             * @type Roo.Element */
33479             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33480                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33481             ]}, true);
33482             if(c.floatable !== false){
33483                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33484                this.collapsedEl.on("click", this.collapseClick, this);
33485             }
33486
33487             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33488                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33489                    id: "message", unselectable: "on", style:{"float":"left"}});
33490                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33491              }
33492             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33493             this.expandBtn.on("click", this.expand, this);
33494         }
33495         if(this.collapseBtn){
33496             this.collapseBtn.setVisible(c.collapsible == true);
33497         }
33498         this.cmargins = c.cmargins || this.cmargins ||
33499                          (this.position == "west" || this.position == "east" ?
33500                              {top: 0, left: 2, right:2, bottom: 0} :
33501                              {top: 2, left: 0, right:0, bottom: 2});
33502         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33503         this.bottomTabs = c.tabPosition != "top";
33504         this.autoScroll = c.autoScroll || false;
33505         if(this.autoScroll){
33506             this.bodyEl.setStyle("overflow", "auto");
33507         }else{
33508             this.bodyEl.setStyle("overflow", "hidden");
33509         }
33510         //if(c.titlebar !== false){
33511             if((!c.titlebar && !c.title) || c.titlebar === false){
33512                 this.titleEl.hide();
33513             }else{
33514                 this.titleEl.show();
33515                 if(c.title){
33516                     this.titleTextEl.innerHTML = c.title;
33517                 }
33518             }
33519         //}
33520         this.duration = c.duration || .30;
33521         this.slideDuration = c.slideDuration || .45;
33522         this.config = c;
33523         if(c.collapsed){
33524             this.collapse(true);
33525         }
33526         if(c.hidden){
33527             this.hide();
33528         }
33529     },
33530     /**
33531      * Returns true if this region is currently visible.
33532      * @return {Boolean}
33533      */
33534     isVisible : function(){
33535         return this.visible;
33536     },
33537
33538     /**
33539      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33540      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
33541      */
33542     setCollapsedTitle : function(title){
33543         title = title || "&#160;";
33544         if(this.collapsedTitleTextEl){
33545             this.collapsedTitleTextEl.innerHTML = title;
33546         }
33547     },
33548
33549     getBox : function(){
33550         var b;
33551         if(!this.collapsed){
33552             b = this.el.getBox(false, true);
33553         }else{
33554             b = this.collapsedEl.getBox(false, true);
33555         }
33556         return b;
33557     },
33558
33559     getMargins : function(){
33560         return this.collapsed ? this.cmargins : this.margins;
33561     },
33562
33563     highlight : function(){
33564         this.el.addClass("x-layout-panel-dragover");
33565     },
33566
33567     unhighlight : function(){
33568         this.el.removeClass("x-layout-panel-dragover");
33569     },
33570
33571     updateBox : function(box){
33572         this.box = box;
33573         if(!this.collapsed){
33574             this.el.dom.style.left = box.x + "px";
33575             this.el.dom.style.top = box.y + "px";
33576             this.updateBody(box.width, box.height);
33577         }else{
33578             this.collapsedEl.dom.style.left = box.x + "px";
33579             this.collapsedEl.dom.style.top = box.y + "px";
33580             this.collapsedEl.setSize(box.width, box.height);
33581         }
33582         if(this.tabs){
33583             this.tabs.autoSizeTabs();
33584         }
33585     },
33586
33587     updateBody : function(w, h){
33588         if(w !== null){
33589             this.el.setWidth(w);
33590             w -= this.el.getBorderWidth("rl");
33591             if(this.config.adjustments){
33592                 w += this.config.adjustments[0];
33593             }
33594         }
33595         if(h !== null){
33596             this.el.setHeight(h);
33597             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33598             h -= this.el.getBorderWidth("tb");
33599             if(this.config.adjustments){
33600                 h += this.config.adjustments[1];
33601             }
33602             this.bodyEl.setHeight(h);
33603             if(this.tabs){
33604                 h = this.tabs.syncHeight(h);
33605             }
33606         }
33607         if(this.panelSize){
33608             w = w !== null ? w : this.panelSize.width;
33609             h = h !== null ? h : this.panelSize.height;
33610         }
33611         if(this.activePanel){
33612             var el = this.activePanel.getEl();
33613             w = w !== null ? w : el.getWidth();
33614             h = h !== null ? h : el.getHeight();
33615             this.panelSize = {width: w, height: h};
33616             this.activePanel.setSize(w, h);
33617         }
33618         if(Roo.isIE && this.tabs){
33619             this.tabs.el.repaint();
33620         }
33621     },
33622
33623     /**
33624      * Returns the container element for this region.
33625      * @return {Roo.Element}
33626      */
33627     getEl : function(){
33628         return this.el;
33629     },
33630
33631     /**
33632      * Hides this region.
33633      */
33634     hide : function(){
33635         if(!this.collapsed){
33636             this.el.dom.style.left = "-2000px";
33637             this.el.hide();
33638         }else{
33639             this.collapsedEl.dom.style.left = "-2000px";
33640             this.collapsedEl.hide();
33641         }
33642         this.visible = false;
33643         this.fireEvent("visibilitychange", this, false);
33644     },
33645
33646     /**
33647      * Shows this region if it was previously hidden.
33648      */
33649     show : function(){
33650         if(!this.collapsed){
33651             this.el.show();
33652         }else{
33653             this.collapsedEl.show();
33654         }
33655         this.visible = true;
33656         this.fireEvent("visibilitychange", this, true);
33657     },
33658
33659     closeClicked : function(){
33660         if(this.activePanel){
33661             this.remove(this.activePanel);
33662         }
33663     },
33664
33665     collapseClick : function(e){
33666         if(this.isSlid){
33667            e.stopPropagation();
33668            this.slideIn();
33669         }else{
33670            e.stopPropagation();
33671            this.slideOut();
33672         }
33673     },
33674
33675     /**
33676      * Collapses this region.
33677      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
33678      */
33679     collapse : function(skipAnim){
33680         if(this.collapsed) return;
33681         this.collapsed = true;
33682         if(this.split){
33683             this.split.el.hide();
33684         }
33685         if(this.config.animate && skipAnim !== true){
33686             this.fireEvent("invalidated", this);
33687             this.animateCollapse();
33688         }else{
33689             this.el.setLocation(-20000,-20000);
33690             this.el.hide();
33691             this.collapsedEl.show();
33692             this.fireEvent("collapsed", this);
33693             this.fireEvent("invalidated", this);
33694         }
33695     },
33696
33697     animateCollapse : function(){
33698         // overridden
33699     },
33700
33701     /**
33702      * Expands this region if it was previously collapsed.
33703      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
33704      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
33705      */
33706     expand : function(e, skipAnim){
33707         if(e) e.stopPropagation();
33708         if(!this.collapsed || this.el.hasActiveFx()) return;
33709         if(this.isSlid){
33710             this.afterSlideIn();
33711             skipAnim = true;
33712         }
33713         this.collapsed = false;
33714         if(this.config.animate && skipAnim !== true){
33715             this.animateExpand();
33716         }else{
33717             this.el.show();
33718             if(this.split){
33719                 this.split.el.show();
33720             }
33721             this.collapsedEl.setLocation(-2000,-2000);
33722             this.collapsedEl.hide();
33723             this.fireEvent("invalidated", this);
33724             this.fireEvent("expanded", this);
33725         }
33726     },
33727
33728     animateExpand : function(){
33729         // overridden
33730     },
33731
33732     initTabs : function()
33733     {
33734         this.bodyEl.setStyle("overflow", "hidden");
33735         var ts = new Roo.TabPanel(
33736                 this.bodyEl.dom,
33737                 {
33738                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
33739                     disableTooltips: this.config.disableTabTips,
33740                     toolbar : this.config.toolbar
33741                 }
33742         );
33743         if(this.config.hideTabs){
33744             ts.stripWrap.setDisplayed(false);
33745         }
33746         this.tabs = ts;
33747         ts.resizeTabs = this.config.resizeTabs === true;
33748         ts.minTabWidth = this.config.minTabWidth || 40;
33749         ts.maxTabWidth = this.config.maxTabWidth || 250;
33750         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33751         ts.monitorResize = false;
33752         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33753         ts.bodyEl.addClass('x-layout-tabs-body');
33754         this.panels.each(this.initPanelAsTab, this);
33755     },
33756
33757     initPanelAsTab : function(panel){
33758         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
33759                     this.config.closeOnTab && panel.isClosable());
33760         if(panel.tabTip !== undefined){
33761             ti.setTooltip(panel.tabTip);
33762         }
33763         ti.on("activate", function(){
33764               this.setActivePanel(panel);
33765         }, this);
33766         if(this.config.closeOnTab){
33767             ti.on("beforeclose", function(t, e){
33768                 e.cancel = true;
33769                 this.remove(panel);
33770             }, this);
33771         }
33772         return ti;
33773     },
33774
33775     updatePanelTitle : function(panel, title){
33776         if(this.activePanel == panel){
33777             this.updateTitle(title);
33778         }
33779         if(this.tabs){
33780             var ti = this.tabs.getTab(panel.getEl().id);
33781             ti.setText(title);
33782             if(panel.tabTip !== undefined){
33783                 ti.setTooltip(panel.tabTip);
33784             }
33785         }
33786     },
33787
33788     updateTitle : function(title){
33789         if(this.titleTextEl && !this.config.title){
33790             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
33791         }
33792     },
33793
33794     setActivePanel : function(panel){
33795         panel = this.getPanel(panel);
33796         if(this.activePanel && this.activePanel != panel){
33797             this.activePanel.setActiveState(false);
33798         }
33799         this.activePanel = panel;
33800         panel.setActiveState(true);
33801         if(this.panelSize){
33802             panel.setSize(this.panelSize.width, this.panelSize.height);
33803         }
33804         if(this.closeBtn){
33805             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33806         }
33807         this.updateTitle(panel.getTitle());
33808         if(this.tabs){
33809             this.fireEvent("invalidated", this);
33810         }
33811         this.fireEvent("panelactivated", this, panel);
33812     },
33813
33814     /**
33815      * Shows the specified panel.
33816      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
33817      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
33818      */
33819     showPanel : function(panel){
33820         if(panel = this.getPanel(panel)){
33821             if(this.tabs){
33822                 var tab = this.tabs.getTab(panel.getEl().id);
33823                 if(tab.isHidden()){
33824                     this.tabs.unhideTab(tab.id);
33825                 }
33826                 tab.activate();
33827             }else{
33828                 this.setActivePanel(panel);
33829             }
33830         }
33831         return panel;
33832     },
33833
33834     /**
33835      * Get the active panel for this region.
33836      * @return {Roo.ContentPanel} The active panel or null
33837      */
33838     getActivePanel : function(){
33839         return this.activePanel;
33840     },
33841
33842     validateVisibility : function(){
33843         if(this.panels.getCount() < 1){
33844             this.updateTitle("&#160;");
33845             this.closeBtn.hide();
33846             this.hide();
33847         }else{
33848             if(!this.isVisible()){
33849                 this.show();
33850             }
33851         }
33852     },
33853
33854     /**
33855      * Adds the passed ContentPanel(s) to this region.
33856      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33857      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
33858      */
33859     add : function(panel){
33860         if(arguments.length > 1){
33861             for(var i = 0, len = arguments.length; i < len; i++) {
33862                 this.add(arguments[i]);
33863             }
33864             return null;
33865         }
33866         if(this.hasPanel(panel)){
33867             this.showPanel(panel);
33868             return panel;
33869         }
33870         panel.setRegion(this);
33871         this.panels.add(panel);
33872         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33873             this.bodyEl.dom.appendChild(panel.getEl().dom);
33874             if(panel.background !== true){
33875                 this.setActivePanel(panel);
33876             }
33877             this.fireEvent("paneladded", this, panel);
33878             return panel;
33879         }
33880         if(!this.tabs){
33881             this.initTabs();
33882         }else{
33883             this.initPanelAsTab(panel);
33884         }
33885         if(panel.background !== true){
33886             this.tabs.activate(panel.getEl().id);
33887         }
33888         this.fireEvent("paneladded", this, panel);
33889         return panel;
33890     },
33891
33892     /**
33893      * Hides the tab for the specified panel.
33894      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33895      */
33896     hidePanel : function(panel){
33897         if(this.tabs && (panel = this.getPanel(panel))){
33898             this.tabs.hideTab(panel.getEl().id);
33899         }
33900     },
33901
33902     /**
33903      * Unhides the tab for a previously hidden panel.
33904      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33905      */
33906     unhidePanel : function(panel){
33907         if(this.tabs && (panel = this.getPanel(panel))){
33908             this.tabs.unhideTab(panel.getEl().id);
33909         }
33910     },
33911
33912     clearPanels : function(){
33913         while(this.panels.getCount() > 0){
33914              this.remove(this.panels.first());
33915         }
33916     },
33917
33918     /**
33919      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33920      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33921      * @param {Boolean} preservePanel Overrides the config preservePanel option
33922      * @return {Roo.ContentPanel} The panel that was removed
33923      */
33924     remove : function(panel, preservePanel){
33925         panel = this.getPanel(panel);
33926         if(!panel){
33927             return null;
33928         }
33929         var e = {};
33930         this.fireEvent("beforeremove", this, panel, e);
33931         if(e.cancel === true){
33932             return null;
33933         }
33934         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33935         var panelId = panel.getId();
33936         this.panels.removeKey(panelId);
33937         if(preservePanel){
33938             document.body.appendChild(panel.getEl().dom);
33939         }
33940         if(this.tabs){
33941             this.tabs.removeTab(panel.getEl().id);
33942         }else if (!preservePanel){
33943             this.bodyEl.dom.removeChild(panel.getEl().dom);
33944         }
33945         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33946             var p = this.panels.first();
33947             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33948             tempEl.appendChild(p.getEl().dom);
33949             this.bodyEl.update("");
33950             this.bodyEl.dom.appendChild(p.getEl().dom);
33951             tempEl = null;
33952             this.updateTitle(p.getTitle());
33953             this.tabs = null;
33954             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33955             this.setActivePanel(p);
33956         }
33957         panel.setRegion(null);
33958         if(this.activePanel == panel){
33959             this.activePanel = null;
33960         }
33961         if(this.config.autoDestroy !== false && preservePanel !== true){
33962             try{panel.destroy();}catch(e){}
33963         }
33964         this.fireEvent("panelremoved", this, panel);
33965         return panel;
33966     },
33967
33968     /**
33969      * Returns the TabPanel component used by this region
33970      * @return {Roo.TabPanel}
33971      */
33972     getTabs : function(){
33973         return this.tabs;
33974     },
33975
33976     createTool : function(parentEl, className){
33977         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
33978             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
33979         btn.addClassOnOver("x-layout-tools-button-over");
33980         return btn;
33981     }
33982 });/*
33983  * Based on:
33984  * Ext JS Library 1.1.1
33985  * Copyright(c) 2006-2007, Ext JS, LLC.
33986  *
33987  * Originally Released Under LGPL - original licence link has changed is not relivant.
33988  *
33989  * Fork - LGPL
33990  * <script type="text/javascript">
33991  */
33992  
33993
33994
33995 /**
33996  * @class Roo.SplitLayoutRegion
33997  * @extends Roo.LayoutRegion
33998  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
33999  */
34000 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
34001     this.cursor = cursor;
34002     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
34003 };
34004
34005 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
34006     splitTip : "Drag to resize.",
34007     collapsibleSplitTip : "Drag to resize. Double click to hide.",
34008     useSplitTips : false,
34009
34010     applyConfig : function(config){
34011         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
34012         if(config.split){
34013             if(!this.split){
34014                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
34015                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
34016                 /** The SplitBar for this region 
34017                 * @type Roo.SplitBar */
34018                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
34019                 this.split.on("moved", this.onSplitMove, this);
34020                 this.split.useShim = config.useShim === true;
34021                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
34022                 if(this.useSplitTips){
34023                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
34024                 }
34025                 if(config.collapsible){
34026                     this.split.el.on("dblclick", this.collapse,  this);
34027                 }
34028             }
34029             if(typeof config.minSize != "undefined"){
34030                 this.split.minSize = config.minSize;
34031             }
34032             if(typeof config.maxSize != "undefined"){
34033                 this.split.maxSize = config.maxSize;
34034             }
34035             if(config.hideWhenEmpty || config.hidden || config.collapsed){
34036                 this.hideSplitter();
34037             }
34038         }
34039     },
34040
34041     getHMaxSize : function(){
34042          var cmax = this.config.maxSize || 10000;
34043          var center = this.mgr.getRegion("center");
34044          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
34045     },
34046
34047     getVMaxSize : function(){
34048          var cmax = this.config.maxSize || 10000;
34049          var center = this.mgr.getRegion("center");
34050          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
34051     },
34052
34053     onSplitMove : function(split, newSize){
34054         this.fireEvent("resized", this, newSize);
34055     },
34056     
34057     /** 
34058      * Returns the {@link Roo.SplitBar} for this region.
34059      * @return {Roo.SplitBar}
34060      */
34061     getSplitBar : function(){
34062         return this.split;
34063     },
34064     
34065     hide : function(){
34066         this.hideSplitter();
34067         Roo.SplitLayoutRegion.superclass.hide.call(this);
34068     },
34069
34070     hideSplitter : function(){
34071         if(this.split){
34072             this.split.el.setLocation(-2000,-2000);
34073             this.split.el.hide();
34074         }
34075     },
34076
34077     show : function(){
34078         if(this.split){
34079             this.split.el.show();
34080         }
34081         Roo.SplitLayoutRegion.superclass.show.call(this);
34082     },
34083     
34084     beforeSlide: function(){
34085         if(Roo.isGecko){// firefox overflow auto bug workaround
34086             this.bodyEl.clip();
34087             if(this.tabs) this.tabs.bodyEl.clip();
34088             if(this.activePanel){
34089                 this.activePanel.getEl().clip();
34090                 
34091                 if(this.activePanel.beforeSlide){
34092                     this.activePanel.beforeSlide();
34093                 }
34094             }
34095         }
34096     },
34097     
34098     afterSlide : function(){
34099         if(Roo.isGecko){// firefox overflow auto bug workaround
34100             this.bodyEl.unclip();
34101             if(this.tabs) this.tabs.bodyEl.unclip();
34102             if(this.activePanel){
34103                 this.activePanel.getEl().unclip();
34104                 if(this.activePanel.afterSlide){
34105                     this.activePanel.afterSlide();
34106                 }
34107             }
34108         }
34109     },
34110
34111     initAutoHide : function(){
34112         if(this.autoHide !== false){
34113             if(!this.autoHideHd){
34114                 var st = new Roo.util.DelayedTask(this.slideIn, this);
34115                 this.autoHideHd = {
34116                     "mouseout": function(e){
34117                         if(!e.within(this.el, true)){
34118                             st.delay(500);
34119                         }
34120                     },
34121                     "mouseover" : function(e){
34122                         st.cancel();
34123                     },
34124                     scope : this
34125                 };
34126             }
34127             this.el.on(this.autoHideHd);
34128         }
34129     },
34130
34131     clearAutoHide : function(){
34132         if(this.autoHide !== false){
34133             this.el.un("mouseout", this.autoHideHd.mouseout);
34134             this.el.un("mouseover", this.autoHideHd.mouseover);
34135         }
34136     },
34137
34138     clearMonitor : function(){
34139         Roo.get(document).un("click", this.slideInIf, this);
34140     },
34141
34142     // these names are backwards but not changed for compat
34143     slideOut : function(){
34144         if(this.isSlid || this.el.hasActiveFx()){
34145             return;
34146         }
34147         this.isSlid = true;
34148         if(this.collapseBtn){
34149             this.collapseBtn.hide();
34150         }
34151         this.closeBtnState = this.closeBtn.getStyle('display');
34152         this.closeBtn.hide();
34153         if(this.stickBtn){
34154             this.stickBtn.show();
34155         }
34156         this.el.show();
34157         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34158         this.beforeSlide();
34159         this.el.setStyle("z-index", 10001);
34160         this.el.slideIn(this.getSlideAnchor(), {
34161             callback: function(){
34162                 this.afterSlide();
34163                 this.initAutoHide();
34164                 Roo.get(document).on("click", this.slideInIf, this);
34165                 this.fireEvent("slideshow", this);
34166             },
34167             scope: this,
34168             block: true
34169         });
34170     },
34171
34172     afterSlideIn : function(){
34173         this.clearAutoHide();
34174         this.isSlid = false;
34175         this.clearMonitor();
34176         this.el.setStyle("z-index", "");
34177         if(this.collapseBtn){
34178             this.collapseBtn.show();
34179         }
34180         this.closeBtn.setStyle('display', this.closeBtnState);
34181         if(this.stickBtn){
34182             this.stickBtn.hide();
34183         }
34184         this.fireEvent("slidehide", this);
34185     },
34186
34187     slideIn : function(cb){
34188         if(!this.isSlid || this.el.hasActiveFx()){
34189             Roo.callback(cb);
34190             return;
34191         }
34192         this.isSlid = false;
34193         this.beforeSlide();
34194         this.el.slideOut(this.getSlideAnchor(), {
34195             callback: function(){
34196                 this.el.setLeftTop(-10000, -10000);
34197                 this.afterSlide();
34198                 this.afterSlideIn();
34199                 Roo.callback(cb);
34200             },
34201             scope: this,
34202             block: true
34203         });
34204     },
34205     
34206     slideInIf : function(e){
34207         if(!e.within(this.el)){
34208             this.slideIn();
34209         }
34210     },
34211
34212     animateCollapse : function(){
34213         this.beforeSlide();
34214         this.el.setStyle("z-index", 20000);
34215         var anchor = this.getSlideAnchor();
34216         this.el.slideOut(anchor, {
34217             callback : function(){
34218                 this.el.setStyle("z-index", "");
34219                 this.collapsedEl.slideIn(anchor, {duration:.3});
34220                 this.afterSlide();
34221                 this.el.setLocation(-10000,-10000);
34222                 this.el.hide();
34223                 this.fireEvent("collapsed", this);
34224             },
34225             scope: this,
34226             block: true
34227         });
34228     },
34229
34230     animateExpand : function(){
34231         this.beforeSlide();
34232         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34233         this.el.setStyle("z-index", 20000);
34234         this.collapsedEl.hide({
34235             duration:.1
34236         });
34237         this.el.slideIn(this.getSlideAnchor(), {
34238             callback : function(){
34239                 this.el.setStyle("z-index", "");
34240                 this.afterSlide();
34241                 if(this.split){
34242                     this.split.el.show();
34243                 }
34244                 this.fireEvent("invalidated", this);
34245                 this.fireEvent("expanded", this);
34246             },
34247             scope: this,
34248             block: true
34249         });
34250     },
34251
34252     anchors : {
34253         "west" : "left",
34254         "east" : "right",
34255         "north" : "top",
34256         "south" : "bottom"
34257     },
34258
34259     sanchors : {
34260         "west" : "l",
34261         "east" : "r",
34262         "north" : "t",
34263         "south" : "b"
34264     },
34265
34266     canchors : {
34267         "west" : "tl-tr",
34268         "east" : "tr-tl",
34269         "north" : "tl-bl",
34270         "south" : "bl-tl"
34271     },
34272
34273     getAnchor : function(){
34274         return this.anchors[this.position];
34275     },
34276
34277     getCollapseAnchor : function(){
34278         return this.canchors[this.position];
34279     },
34280
34281     getSlideAnchor : function(){
34282         return this.sanchors[this.position];
34283     },
34284
34285     getAlignAdj : function(){
34286         var cm = this.cmargins;
34287         switch(this.position){
34288             case "west":
34289                 return [0, 0];
34290             break;
34291             case "east":
34292                 return [0, 0];
34293             break;
34294             case "north":
34295                 return [0, 0];
34296             break;
34297             case "south":
34298                 return [0, 0];
34299             break;
34300         }
34301     },
34302
34303     getExpandAdj : function(){
34304         var c = this.collapsedEl, cm = this.cmargins;
34305         switch(this.position){
34306             case "west":
34307                 return [-(cm.right+c.getWidth()+cm.left), 0];
34308             break;
34309             case "east":
34310                 return [cm.right+c.getWidth()+cm.left, 0];
34311             break;
34312             case "north":
34313                 return [0, -(cm.top+cm.bottom+c.getHeight())];
34314             break;
34315             case "south":
34316                 return [0, cm.top+cm.bottom+c.getHeight()];
34317             break;
34318         }
34319     }
34320 });/*
34321  * Based on:
34322  * Ext JS Library 1.1.1
34323  * Copyright(c) 2006-2007, Ext JS, LLC.
34324  *
34325  * Originally Released Under LGPL - original licence link has changed is not relivant.
34326  *
34327  * Fork - LGPL
34328  * <script type="text/javascript">
34329  */
34330 /*
34331  * These classes are private internal classes
34332  */
34333 Roo.CenterLayoutRegion = function(mgr, config){
34334     Roo.LayoutRegion.call(this, mgr, config, "center");
34335     this.visible = true;
34336     this.minWidth = config.minWidth || 20;
34337     this.minHeight = config.minHeight || 20;
34338 };
34339
34340 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
34341     hide : function(){
34342         // center panel can't be hidden
34343     },
34344     
34345     show : function(){
34346         // center panel can't be hidden
34347     },
34348     
34349     getMinWidth: function(){
34350         return this.minWidth;
34351     },
34352     
34353     getMinHeight: function(){
34354         return this.minHeight;
34355     }
34356 });
34357
34358
34359 Roo.NorthLayoutRegion = function(mgr, config){
34360     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
34361     if(this.split){
34362         this.split.placement = Roo.SplitBar.TOP;
34363         this.split.orientation = Roo.SplitBar.VERTICAL;
34364         this.split.el.addClass("x-layout-split-v");
34365     }
34366     var size = config.initialSize || config.height;
34367     if(typeof size != "undefined"){
34368         this.el.setHeight(size);
34369     }
34370 };
34371 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
34372     orientation: Roo.SplitBar.VERTICAL,
34373     getBox : function(){
34374         if(this.collapsed){
34375             return this.collapsedEl.getBox();
34376         }
34377         var box = this.el.getBox();
34378         if(this.split){
34379             box.height += this.split.el.getHeight();
34380         }
34381         return box;
34382     },
34383     
34384     updateBox : function(box){
34385         if(this.split && !this.collapsed){
34386             box.height -= this.split.el.getHeight();
34387             this.split.el.setLeft(box.x);
34388             this.split.el.setTop(box.y+box.height);
34389             this.split.el.setWidth(box.width);
34390         }
34391         if(this.collapsed){
34392             this.updateBody(box.width, null);
34393         }
34394         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34395     }
34396 });
34397
34398 Roo.SouthLayoutRegion = function(mgr, config){
34399     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
34400     if(this.split){
34401         this.split.placement = Roo.SplitBar.BOTTOM;
34402         this.split.orientation = Roo.SplitBar.VERTICAL;
34403         this.split.el.addClass("x-layout-split-v");
34404     }
34405     var size = config.initialSize || config.height;
34406     if(typeof size != "undefined"){
34407         this.el.setHeight(size);
34408     }
34409 };
34410 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
34411     orientation: Roo.SplitBar.VERTICAL,
34412     getBox : function(){
34413         if(this.collapsed){
34414             return this.collapsedEl.getBox();
34415         }
34416         var box = this.el.getBox();
34417         if(this.split){
34418             var sh = this.split.el.getHeight();
34419             box.height += sh;
34420             box.y -= sh;
34421         }
34422         return box;
34423     },
34424     
34425     updateBox : function(box){
34426         if(this.split && !this.collapsed){
34427             var sh = this.split.el.getHeight();
34428             box.height -= sh;
34429             box.y += sh;
34430             this.split.el.setLeft(box.x);
34431             this.split.el.setTop(box.y-sh);
34432             this.split.el.setWidth(box.width);
34433         }
34434         if(this.collapsed){
34435             this.updateBody(box.width, null);
34436         }
34437         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34438     }
34439 });
34440
34441 Roo.EastLayoutRegion = function(mgr, config){
34442     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
34443     if(this.split){
34444         this.split.placement = Roo.SplitBar.RIGHT;
34445         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34446         this.split.el.addClass("x-layout-split-h");
34447     }
34448     var size = config.initialSize || config.width;
34449     if(typeof size != "undefined"){
34450         this.el.setWidth(size);
34451     }
34452 };
34453 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
34454     orientation: Roo.SplitBar.HORIZONTAL,
34455     getBox : function(){
34456         if(this.collapsed){
34457             return this.collapsedEl.getBox();
34458         }
34459         var box = this.el.getBox();
34460         if(this.split){
34461             var sw = this.split.el.getWidth();
34462             box.width += sw;
34463             box.x -= sw;
34464         }
34465         return box;
34466     },
34467
34468     updateBox : function(box){
34469         if(this.split && !this.collapsed){
34470             var sw = this.split.el.getWidth();
34471             box.width -= sw;
34472             this.split.el.setLeft(box.x);
34473             this.split.el.setTop(box.y);
34474             this.split.el.setHeight(box.height);
34475             box.x += sw;
34476         }
34477         if(this.collapsed){
34478             this.updateBody(null, box.height);
34479         }
34480         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34481     }
34482 });
34483
34484 Roo.WestLayoutRegion = function(mgr, config){
34485     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
34486     if(this.split){
34487         this.split.placement = Roo.SplitBar.LEFT;
34488         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34489         this.split.el.addClass("x-layout-split-h");
34490     }
34491     var size = config.initialSize || config.width;
34492     if(typeof size != "undefined"){
34493         this.el.setWidth(size);
34494     }
34495 };
34496 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
34497     orientation: Roo.SplitBar.HORIZONTAL,
34498     getBox : function(){
34499         if(this.collapsed){
34500             return this.collapsedEl.getBox();
34501         }
34502         var box = this.el.getBox();
34503         if(this.split){
34504             box.width += this.split.el.getWidth();
34505         }
34506         return box;
34507     },
34508     
34509     updateBox : function(box){
34510         if(this.split && !this.collapsed){
34511             var sw = this.split.el.getWidth();
34512             box.width -= sw;
34513             this.split.el.setLeft(box.x+box.width);
34514             this.split.el.setTop(box.y);
34515             this.split.el.setHeight(box.height);
34516         }
34517         if(this.collapsed){
34518             this.updateBody(null, box.height);
34519         }
34520         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34521     }
34522 });
34523 /*
34524  * Based on:
34525  * Ext JS Library 1.1.1
34526  * Copyright(c) 2006-2007, Ext JS, LLC.
34527  *
34528  * Originally Released Under LGPL - original licence link has changed is not relivant.
34529  *
34530  * Fork - LGPL
34531  * <script type="text/javascript">
34532  */
34533  
34534  
34535 /*
34536  * Private internal class for reading and applying state
34537  */
34538 Roo.LayoutStateManager = function(layout){
34539      // default empty state
34540      this.state = {
34541         north: {},
34542         south: {},
34543         east: {},
34544         west: {}       
34545     };
34546 };
34547
34548 Roo.LayoutStateManager.prototype = {
34549     init : function(layout, provider){
34550         this.provider = provider;
34551         var state = provider.get(layout.id+"-layout-state");
34552         if(state){
34553             var wasUpdating = layout.isUpdating();
34554             if(!wasUpdating){
34555                 layout.beginUpdate();
34556             }
34557             for(var key in state){
34558                 if(typeof state[key] != "function"){
34559                     var rstate = state[key];
34560                     var r = layout.getRegion(key);
34561                     if(r && rstate){
34562                         if(rstate.size){
34563                             r.resizeTo(rstate.size);
34564                         }
34565                         if(rstate.collapsed == true){
34566                             r.collapse(true);
34567                         }else{
34568                             r.expand(null, true);
34569                         }
34570                     }
34571                 }
34572             }
34573             if(!wasUpdating){
34574                 layout.endUpdate();
34575             }
34576             this.state = state; 
34577         }
34578         this.layout = layout;
34579         layout.on("regionresized", this.onRegionResized, this);
34580         layout.on("regioncollapsed", this.onRegionCollapsed, this);
34581         layout.on("regionexpanded", this.onRegionExpanded, this);
34582     },
34583     
34584     storeState : function(){
34585         this.provider.set(this.layout.id+"-layout-state", this.state);
34586     },
34587     
34588     onRegionResized : function(region, newSize){
34589         this.state[region.getPosition()].size = newSize;
34590         this.storeState();
34591     },
34592     
34593     onRegionCollapsed : function(region){
34594         this.state[region.getPosition()].collapsed = true;
34595         this.storeState();
34596     },
34597     
34598     onRegionExpanded : function(region){
34599         this.state[region.getPosition()].collapsed = false;
34600         this.storeState();
34601     }
34602 };/*
34603  * Based on:
34604  * Ext JS Library 1.1.1
34605  * Copyright(c) 2006-2007, Ext JS, LLC.
34606  *
34607  * Originally Released Under LGPL - original licence link has changed is not relivant.
34608  *
34609  * Fork - LGPL
34610  * <script type="text/javascript">
34611  */
34612 /**
34613  * @class Roo.ContentPanel
34614  * @extends Roo.util.Observable
34615  * A basic ContentPanel element.
34616  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
34617  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
34618  * @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
34619  * @cfg {Boolean}   closable      True if the panel can be closed/removed
34620  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
34621  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34622  * @cfg {Toolbar}   toolbar       A toolbar for this panel
34623  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
34624  * @cfg {String} title          The title for this panel
34625  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34626  * @cfg {String} url            Calls {@link #setUrl} with this value
34627  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34628  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
34629  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
34630  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
34631
34632  * @constructor
34633  * Create a new ContentPanel.
34634  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34635  * @param {String/Object} config A string to set only the title or a config object
34636  * @param {String} content (optional) Set the HTML content for this panel
34637  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34638  */
34639 Roo.ContentPanel = function(el, config, content){
34640     
34641      
34642     /*
34643     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
34644         config = el;
34645         el = Roo.id();
34646     }
34647     if (config && config.parentLayout) { 
34648         el = config.parentLayout.el.createChild(); 
34649     }
34650     */
34651     if(el.autoCreate){ // xtype is available if this is called from factory
34652         config = el;
34653         el = Roo.id();
34654     }
34655     this.el = Roo.get(el);
34656     if(!this.el && config && config.autoCreate){
34657         if(typeof config.autoCreate == "object"){
34658             if(!config.autoCreate.id){
34659                 config.autoCreate.id = config.id||el;
34660             }
34661             this.el = Roo.DomHelper.append(document.body,
34662                         config.autoCreate, true);
34663         }else{
34664             this.el = Roo.DomHelper.append(document.body,
34665                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
34666         }
34667     }
34668     this.closable = false;
34669     this.loaded = false;
34670     this.active = false;
34671     if(typeof config == "string"){
34672         this.title = config;
34673     }else{
34674         Roo.apply(this, config);
34675     }
34676     
34677     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
34678         this.wrapEl = this.el.wrap();
34679         this.toolbar.container = this.el.insertSibling(false, 'before');
34680         this.toolbar = new Roo.Toolbar(this.toolbar);
34681     }
34682     
34683     // xtype created footer. - not sure if will work as we normally have to render first..
34684     if (this.footer && !this.footer.el && this.footer.xtype) {
34685         if (!this.wrapEl) {
34686             this.wrapEl = this.el.wrap();
34687         }
34688     
34689         this.footer.container = this.wrapEl.createChild();
34690          
34691         this.footer = Roo.factory(this.footer, Roo);
34692         
34693     }
34694     
34695     if(this.resizeEl){
34696         this.resizeEl = Roo.get(this.resizeEl, true);
34697     }else{
34698         this.resizeEl = this.el;
34699     }
34700     // handle view.xtype
34701     
34702  
34703     
34704     
34705     this.addEvents({
34706         /**
34707          * @event activate
34708          * Fires when this panel is activated. 
34709          * @param {Roo.ContentPanel} this
34710          */
34711         "activate" : true,
34712         /**
34713          * @event deactivate
34714          * Fires when this panel is activated. 
34715          * @param {Roo.ContentPanel} this
34716          */
34717         "deactivate" : true,
34718
34719         /**
34720          * @event resize
34721          * Fires when this panel is resized if fitToFrame is true.
34722          * @param {Roo.ContentPanel} this
34723          * @param {Number} width The width after any component adjustments
34724          * @param {Number} height The height after any component adjustments
34725          */
34726         "resize" : true,
34727         
34728          /**
34729          * @event render
34730          * Fires when this tab is created
34731          * @param {Roo.ContentPanel} this
34732          */
34733         "render" : true
34734         
34735         
34736         
34737     });
34738     
34739
34740     
34741     
34742     if(this.autoScroll){
34743         this.resizeEl.setStyle("overflow", "auto");
34744     } else {
34745         // fix randome scrolling
34746         this.el.on('scroll', function() {
34747             Roo.log('fix random scolling');
34748             this.scrollTo('top',0); 
34749         });
34750     }
34751     content = content || this.content;
34752     if(content){
34753         this.setContent(content);
34754     }
34755     if(config && config.url){
34756         this.setUrl(this.url, this.params, this.loadOnce);
34757     }
34758     
34759     
34760     
34761     Roo.ContentPanel.superclass.constructor.call(this);
34762     
34763     if (this.view && typeof(this.view.xtype) != 'undefined') {
34764         this.view.el = this.el.appendChild(document.createElement("div"));
34765         this.view = Roo.factory(this.view); 
34766         this.view.render  &&  this.view.render(false, '');  
34767     }
34768     
34769     
34770     this.fireEvent('render', this);
34771 };
34772
34773 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
34774     tabTip:'',
34775     setRegion : function(region){
34776         this.region = region;
34777         if(region){
34778            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
34779         }else{
34780            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
34781         } 
34782     },
34783     
34784     /**
34785      * Returns the toolbar for this Panel if one was configured. 
34786      * @return {Roo.Toolbar} 
34787      */
34788     getToolbar : function(){
34789         return this.toolbar;
34790     },
34791     
34792     setActiveState : function(active){
34793         this.active = active;
34794         if(!active){
34795             this.fireEvent("deactivate", this);
34796         }else{
34797             this.fireEvent("activate", this);
34798         }
34799     },
34800     /**
34801      * Updates this panel's element
34802      * @param {String} content The new content
34803      * @param {Boolean} loadScripts (optional) true to look for and process scripts
34804     */
34805     setContent : function(content, loadScripts){
34806         this.el.update(content, loadScripts);
34807     },
34808
34809     ignoreResize : function(w, h){
34810         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
34811             return true;
34812         }else{
34813             this.lastSize = {width: w, height: h};
34814             return false;
34815         }
34816     },
34817     /**
34818      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
34819      * @return {Roo.UpdateManager} The UpdateManager
34820      */
34821     getUpdateManager : function(){
34822         return this.el.getUpdateManager();
34823     },
34824      /**
34825      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
34826      * @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:
34827 <pre><code>
34828 panel.load({
34829     url: "your-url.php",
34830     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
34831     callback: yourFunction,
34832     scope: yourObject, //(optional scope)
34833     discardUrl: false,
34834     nocache: false,
34835     text: "Loading...",
34836     timeout: 30,
34837     scripts: false
34838 });
34839 </code></pre>
34840      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
34841      * 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.
34842      * @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}
34843      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
34844      * @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.
34845      * @return {Roo.ContentPanel} this
34846      */
34847     load : function(){
34848         var um = this.el.getUpdateManager();
34849         um.update.apply(um, arguments);
34850         return this;
34851     },
34852
34853
34854     /**
34855      * 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.
34856      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
34857      * @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)
34858      * @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)
34859      * @return {Roo.UpdateManager} The UpdateManager
34860      */
34861     setUrl : function(url, params, loadOnce){
34862         if(this.refreshDelegate){
34863             this.removeListener("activate", this.refreshDelegate);
34864         }
34865         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34866         this.on("activate", this.refreshDelegate);
34867         return this.el.getUpdateManager();
34868     },
34869     
34870     _handleRefresh : function(url, params, loadOnce){
34871         if(!loadOnce || !this.loaded){
34872             var updater = this.el.getUpdateManager();
34873             updater.update(url, params, this._setLoaded.createDelegate(this));
34874         }
34875     },
34876     
34877     _setLoaded : function(){
34878         this.loaded = true;
34879     }, 
34880     
34881     /**
34882      * Returns this panel's id
34883      * @return {String} 
34884      */
34885     getId : function(){
34886         return this.el.id;
34887     },
34888     
34889     /** 
34890      * Returns this panel's element - used by regiosn to add.
34891      * @return {Roo.Element} 
34892      */
34893     getEl : function(){
34894         return this.wrapEl || this.el;
34895     },
34896     
34897     adjustForComponents : function(width, height)
34898     {
34899         //Roo.log('adjustForComponents ');
34900         if(this.resizeEl != this.el){
34901             width -= this.el.getFrameWidth('lr');
34902             height -= this.el.getFrameWidth('tb');
34903         }
34904         if(this.toolbar){
34905             var te = this.toolbar.getEl();
34906             height -= te.getHeight();
34907             te.setWidth(width);
34908         }
34909         if(this.footer){
34910             var te = this.footer.getEl();
34911             Roo.log("footer:" + te.getHeight());
34912             
34913             height -= te.getHeight();
34914             te.setWidth(width);
34915         }
34916         
34917         
34918         if(this.adjustments){
34919             width += this.adjustments[0];
34920             height += this.adjustments[1];
34921         }
34922         return {"width": width, "height": height};
34923     },
34924     
34925     setSize : function(width, height){
34926         if(this.fitToFrame && !this.ignoreResize(width, height)){
34927             if(this.fitContainer && this.resizeEl != this.el){
34928                 this.el.setSize(width, height);
34929             }
34930             var size = this.adjustForComponents(width, height);
34931             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34932             this.fireEvent('resize', this, size.width, size.height);
34933         }
34934     },
34935     
34936     /**
34937      * Returns this panel's title
34938      * @return {String} 
34939      */
34940     getTitle : function(){
34941         return this.title;
34942     },
34943     
34944     /**
34945      * Set this panel's title
34946      * @param {String} title
34947      */
34948     setTitle : function(title){
34949         this.title = title;
34950         if(this.region){
34951             this.region.updatePanelTitle(this, title);
34952         }
34953     },
34954     
34955     /**
34956      * Returns true is this panel was configured to be closable
34957      * @return {Boolean} 
34958      */
34959     isClosable : function(){
34960         return this.closable;
34961     },
34962     
34963     beforeSlide : function(){
34964         this.el.clip();
34965         this.resizeEl.clip();
34966     },
34967     
34968     afterSlide : function(){
34969         this.el.unclip();
34970         this.resizeEl.unclip();
34971     },
34972     
34973     /**
34974      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
34975      *   Will fail silently if the {@link #setUrl} method has not been called.
34976      *   This does not activate the panel, just updates its content.
34977      */
34978     refresh : function(){
34979         if(this.refreshDelegate){
34980            this.loaded = false;
34981            this.refreshDelegate();
34982         }
34983     },
34984     
34985     /**
34986      * Destroys this panel
34987      */
34988     destroy : function(){
34989         this.el.removeAllListeners();
34990         var tempEl = document.createElement("span");
34991         tempEl.appendChild(this.el.dom);
34992         tempEl.innerHTML = "";
34993         this.el.remove();
34994         this.el = null;
34995     },
34996     
34997     /**
34998      * form - if the content panel contains a form - this is a reference to it.
34999      * @type {Roo.form.Form}
35000      */
35001     form : false,
35002     /**
35003      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
35004      *    This contains a reference to it.
35005      * @type {Roo.View}
35006      */
35007     view : false,
35008     
35009       /**
35010      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
35011      * <pre><code>
35012
35013 layout.addxtype({
35014        xtype : 'Form',
35015        items: [ .... ]
35016    }
35017 );
35018
35019 </code></pre>
35020      * @param {Object} cfg Xtype definition of item to add.
35021      */
35022     
35023     addxtype : function(cfg) {
35024         // add form..
35025         if (cfg.xtype.match(/^Form$/)) {
35026             
35027             var el;
35028             //if (this.footer) {
35029             //    el = this.footer.container.insertSibling(false, 'before');
35030             //} else {
35031                 el = this.el.createChild();
35032             //}
35033
35034             this.form = new  Roo.form.Form(cfg);
35035             
35036             
35037             if ( this.form.allItems.length) this.form.render(el.dom);
35038             return this.form;
35039         }
35040         // should only have one of theses..
35041         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
35042             // views.. should not be just added - used named prop 'view''
35043             
35044             cfg.el = this.el.appendChild(document.createElement("div"));
35045             // factory?
35046             
35047             var ret = new Roo.factory(cfg);
35048              
35049              ret.render && ret.render(false, ''); // render blank..
35050             this.view = ret;
35051             return ret;
35052         }
35053         return false;
35054     }
35055 });
35056
35057 /**
35058  * @class Roo.GridPanel
35059  * @extends Roo.ContentPanel
35060  * @constructor
35061  * Create a new GridPanel.
35062  * @param {Roo.grid.Grid} grid The grid for this panel
35063  * @param {String/Object} config A string to set only the panel's title, or a config object
35064  */
35065 Roo.GridPanel = function(grid, config){
35066     
35067   
35068     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
35069         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
35070         
35071     this.wrapper.dom.appendChild(grid.getGridEl().dom);
35072     
35073     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
35074     
35075     if(this.toolbar){
35076         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
35077     }
35078     // xtype created footer. - not sure if will work as we normally have to render first..
35079     if (this.footer && !this.footer.el && this.footer.xtype) {
35080         
35081         this.footer.container = this.grid.getView().getFooterPanel(true);
35082         this.footer.dataSource = this.grid.dataSource;
35083         this.footer = Roo.factory(this.footer, Roo);
35084         
35085     }
35086     
35087     grid.monitorWindowResize = false; // turn off autosizing
35088     grid.autoHeight = false;
35089     grid.autoWidth = false;
35090     this.grid = grid;
35091     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
35092 };
35093
35094 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
35095     getId : function(){
35096         return this.grid.id;
35097     },
35098     
35099     /**
35100      * Returns the grid for this panel
35101      * @return {Roo.grid.Grid} 
35102      */
35103     getGrid : function(){
35104         return this.grid;    
35105     },
35106     
35107     setSize : function(width, height){
35108         if(!this.ignoreResize(width, height)){
35109             var grid = this.grid;
35110             var size = this.adjustForComponents(width, height);
35111             grid.getGridEl().setSize(size.width, size.height);
35112             grid.autoSize();
35113         }
35114     },
35115     
35116     beforeSlide : function(){
35117         this.grid.getView().scroller.clip();
35118     },
35119     
35120     afterSlide : function(){
35121         this.grid.getView().scroller.unclip();
35122     },
35123     
35124     destroy : function(){
35125         this.grid.destroy();
35126         delete this.grid;
35127         Roo.GridPanel.superclass.destroy.call(this); 
35128     }
35129 });
35130
35131
35132 /**
35133  * @class Roo.NestedLayoutPanel
35134  * @extends Roo.ContentPanel
35135  * @constructor
35136  * Create a new NestedLayoutPanel.
35137  * 
35138  * 
35139  * @param {Roo.BorderLayout} layout The layout for this panel
35140  * @param {String/Object} config A string to set only the title or a config object
35141  */
35142 Roo.NestedLayoutPanel = function(layout, config)
35143 {
35144     // construct with only one argument..
35145     /* FIXME - implement nicer consturctors
35146     if (layout.layout) {
35147         config = layout;
35148         layout = config.layout;
35149         delete config.layout;
35150     }
35151     if (layout.xtype && !layout.getEl) {
35152         // then layout needs constructing..
35153         layout = Roo.factory(layout, Roo);
35154     }
35155     */
35156     
35157     
35158     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
35159     
35160     layout.monitorWindowResize = false; // turn off autosizing
35161     this.layout = layout;
35162     this.layout.getEl().addClass("x-layout-nested-layout");
35163     
35164     
35165     
35166     
35167 };
35168
35169 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
35170
35171     setSize : function(width, height){
35172         if(!this.ignoreResize(width, height)){
35173             var size = this.adjustForComponents(width, height);
35174             var el = this.layout.getEl();
35175             el.setSize(size.width, size.height);
35176             var touch = el.dom.offsetWidth;
35177             this.layout.layout();
35178             // ie requires a double layout on the first pass
35179             if(Roo.isIE && !this.initialized){
35180                 this.initialized = true;
35181                 this.layout.layout();
35182             }
35183         }
35184     },
35185     
35186     // activate all subpanels if not currently active..
35187     
35188     setActiveState : function(active){
35189         this.active = active;
35190         if(!active){
35191             this.fireEvent("deactivate", this);
35192             return;
35193         }
35194         
35195         this.fireEvent("activate", this);
35196         // not sure if this should happen before or after..
35197         if (!this.layout) {
35198             return; // should not happen..
35199         }
35200         var reg = false;
35201         for (var r in this.layout.regions) {
35202             reg = this.layout.getRegion(r);
35203             if (reg.getActivePanel()) {
35204                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
35205                 reg.setActivePanel(reg.getActivePanel());
35206                 continue;
35207             }
35208             if (!reg.panels.length) {
35209                 continue;
35210             }
35211             reg.showPanel(reg.getPanel(0));
35212         }
35213         
35214         
35215         
35216         
35217     },
35218     
35219     /**
35220      * Returns the nested BorderLayout for this panel
35221      * @return {Roo.BorderLayout} 
35222      */
35223     getLayout : function(){
35224         return this.layout;
35225     },
35226     
35227      /**
35228      * Adds a xtype elements to the layout of the nested panel
35229      * <pre><code>
35230
35231 panel.addxtype({
35232        xtype : 'ContentPanel',
35233        region: 'west',
35234        items: [ .... ]
35235    }
35236 );
35237
35238 panel.addxtype({
35239         xtype : 'NestedLayoutPanel',
35240         region: 'west',
35241         layout: {
35242            center: { },
35243            west: { }   
35244         },
35245         items : [ ... list of content panels or nested layout panels.. ]
35246    }
35247 );
35248 </code></pre>
35249      * @param {Object} cfg Xtype definition of item to add.
35250      */
35251     addxtype : function(cfg) {
35252         return this.layout.addxtype(cfg);
35253     
35254     }
35255 });
35256
35257 Roo.ScrollPanel = function(el, config, content){
35258     config = config || {};
35259     config.fitToFrame = true;
35260     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
35261     
35262     this.el.dom.style.overflow = "hidden";
35263     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
35264     this.el.removeClass("x-layout-inactive-content");
35265     this.el.on("mousewheel", this.onWheel, this);
35266
35267     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
35268     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
35269     up.unselectable(); down.unselectable();
35270     up.on("click", this.scrollUp, this);
35271     down.on("click", this.scrollDown, this);
35272     up.addClassOnOver("x-scroller-btn-over");
35273     down.addClassOnOver("x-scroller-btn-over");
35274     up.addClassOnClick("x-scroller-btn-click");
35275     down.addClassOnClick("x-scroller-btn-click");
35276     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
35277
35278     this.resizeEl = this.el;
35279     this.el = wrap; this.up = up; this.down = down;
35280 };
35281
35282 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
35283     increment : 100,
35284     wheelIncrement : 5,
35285     scrollUp : function(){
35286         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
35287     },
35288
35289     scrollDown : function(){
35290         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
35291     },
35292
35293     afterScroll : function(){
35294         var el = this.resizeEl;
35295         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
35296         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35297         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35298     },
35299
35300     setSize : function(){
35301         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
35302         this.afterScroll();
35303     },
35304
35305     onWheel : function(e){
35306         var d = e.getWheelDelta();
35307         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
35308         this.afterScroll();
35309         e.stopEvent();
35310     },
35311
35312     setContent : function(content, loadScripts){
35313         this.resizeEl.update(content, loadScripts);
35314     }
35315
35316 });
35317
35318
35319
35320
35321
35322
35323
35324
35325
35326 /**
35327  * @class Roo.TreePanel
35328  * @extends Roo.ContentPanel
35329  * @constructor
35330  * Create a new TreePanel. - defaults to fit/scoll contents.
35331  * @param {String/Object} config A string to set only the panel's title, or a config object
35332  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
35333  */
35334 Roo.TreePanel = function(config){
35335     var el = config.el;
35336     var tree = config.tree;
35337     delete config.tree; 
35338     delete config.el; // hopefull!
35339     
35340     // wrapper for IE7 strict & safari scroll issue
35341     
35342     var treeEl = el.createChild();
35343     config.resizeEl = treeEl;
35344     
35345     
35346     
35347     Roo.TreePanel.superclass.constructor.call(this, el, config);
35348  
35349  
35350     this.tree = new Roo.tree.TreePanel(treeEl , tree);
35351     //console.log(tree);
35352     this.on('activate', function()
35353     {
35354         if (this.tree.rendered) {
35355             return;
35356         }
35357         //console.log('render tree');
35358         this.tree.render();
35359     });
35360     // this should not be needed.. - it's actually the 'el' that resizes?
35361     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
35362     
35363     //this.on('resize',  function (cp, w, h) {
35364     //        this.tree.innerCt.setWidth(w);
35365     //        this.tree.innerCt.setHeight(h);
35366     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
35367     //});
35368
35369         
35370     
35371 };
35372
35373 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
35374     fitToFrame : true,
35375     autoScroll : true
35376 });
35377
35378
35379
35380
35381
35382
35383
35384
35385
35386
35387
35388 /*
35389  * Based on:
35390  * Ext JS Library 1.1.1
35391  * Copyright(c) 2006-2007, Ext JS, LLC.
35392  *
35393  * Originally Released Under LGPL - original licence link has changed is not relivant.
35394  *
35395  * Fork - LGPL
35396  * <script type="text/javascript">
35397  */
35398  
35399
35400 /**
35401  * @class Roo.ReaderLayout
35402  * @extends Roo.BorderLayout
35403  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
35404  * center region containing two nested regions (a top one for a list view and one for item preview below),
35405  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
35406  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
35407  * expedites the setup of the overall layout and regions for this common application style.
35408  * Example:
35409  <pre><code>
35410 var reader = new Roo.ReaderLayout();
35411 var CP = Roo.ContentPanel;  // shortcut for adding
35412
35413 reader.beginUpdate();
35414 reader.add("north", new CP("north", "North"));
35415 reader.add("west", new CP("west", {title: "West"}));
35416 reader.add("east", new CP("east", {title: "East"}));
35417
35418 reader.regions.listView.add(new CP("listView", "List"));
35419 reader.regions.preview.add(new CP("preview", "Preview"));
35420 reader.endUpdate();
35421 </code></pre>
35422 * @constructor
35423 * Create a new ReaderLayout
35424 * @param {Object} config Configuration options
35425 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
35426 * document.body if omitted)
35427 */
35428 Roo.ReaderLayout = function(config, renderTo){
35429     var c = config || {size:{}};
35430     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
35431         north: c.north !== false ? Roo.apply({
35432             split:false,
35433             initialSize: 32,
35434             titlebar: false
35435         }, c.north) : false,
35436         west: c.west !== false ? Roo.apply({
35437             split:true,
35438             initialSize: 200,
35439             minSize: 175,
35440             maxSize: 400,
35441             titlebar: true,
35442             collapsible: true,
35443             animate: true,
35444             margins:{left:5,right:0,bottom:5,top:5},
35445             cmargins:{left:5,right:5,bottom:5,top:5}
35446         }, c.west) : false,
35447         east: c.east !== false ? Roo.apply({
35448             split:true,
35449             initialSize: 200,
35450             minSize: 175,
35451             maxSize: 400,
35452             titlebar: true,
35453             collapsible: true,
35454             animate: true,
35455             margins:{left:0,right:5,bottom:5,top:5},
35456             cmargins:{left:5,right:5,bottom:5,top:5}
35457         }, c.east) : false,
35458         center: Roo.apply({
35459             tabPosition: 'top',
35460             autoScroll:false,
35461             closeOnTab: true,
35462             titlebar:false,
35463             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
35464         }, c.center)
35465     });
35466
35467     this.el.addClass('x-reader');
35468
35469     this.beginUpdate();
35470
35471     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
35472         south: c.preview !== false ? Roo.apply({
35473             split:true,
35474             initialSize: 200,
35475             minSize: 100,
35476             autoScroll:true,
35477             collapsible:true,
35478             titlebar: true,
35479             cmargins:{top:5,left:0, right:0, bottom:0}
35480         }, c.preview) : false,
35481         center: Roo.apply({
35482             autoScroll:false,
35483             titlebar:false,
35484             minHeight:200
35485         }, c.listView)
35486     });
35487     this.add('center', new Roo.NestedLayoutPanel(inner,
35488             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
35489
35490     this.endUpdate();
35491
35492     this.regions.preview = inner.getRegion('south');
35493     this.regions.listView = inner.getRegion('center');
35494 };
35495
35496 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
35497  * Based on:
35498  * Ext JS Library 1.1.1
35499  * Copyright(c) 2006-2007, Ext JS, LLC.
35500  *
35501  * Originally Released Under LGPL - original licence link has changed is not relivant.
35502  *
35503  * Fork - LGPL
35504  * <script type="text/javascript">
35505  */
35506  
35507 /**
35508  * @class Roo.grid.Grid
35509  * @extends Roo.util.Observable
35510  * This class represents the primary interface of a component based grid control.
35511  * <br><br>Usage:<pre><code>
35512  var grid = new Roo.grid.Grid("my-container-id", {
35513      ds: myDataStore,
35514      cm: myColModel,
35515      selModel: mySelectionModel,
35516      autoSizeColumns: true,
35517      monitorWindowResize: false,
35518      trackMouseOver: true
35519  });
35520  // set any options
35521  grid.render();
35522  * </code></pre>
35523  * <b>Common Problems:</b><br/>
35524  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
35525  * element will correct this<br/>
35526  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
35527  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
35528  * are unpredictable.<br/>
35529  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
35530  * grid to calculate dimensions/offsets.<br/>
35531   * @constructor
35532  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
35533  * The container MUST have some type of size defined for the grid to fill. The container will be
35534  * automatically set to position relative if it isn't already.
35535  * @param {Object} config A config object that sets properties on this grid.
35536  */
35537 Roo.grid.Grid = function(container, config){
35538         // initialize the container
35539         this.container = Roo.get(container);
35540         this.container.update("");
35541         this.container.setStyle("overflow", "hidden");
35542     this.container.addClass('x-grid-container');
35543
35544     this.id = this.container.id;
35545
35546     Roo.apply(this, config);
35547     // check and correct shorthanded configs
35548     if(this.ds){
35549         this.dataSource = this.ds;
35550         delete this.ds;
35551     }
35552     if(this.cm){
35553         this.colModel = this.cm;
35554         delete this.cm;
35555     }
35556     if(this.sm){
35557         this.selModel = this.sm;
35558         delete this.sm;
35559     }
35560
35561     if (this.selModel) {
35562         this.selModel = Roo.factory(this.selModel, Roo.grid);
35563         this.sm = this.selModel;
35564         this.sm.xmodule = this.xmodule || false;
35565     }
35566     if (typeof(this.colModel.config) == 'undefined') {
35567         this.colModel = new Roo.grid.ColumnModel(this.colModel);
35568         this.cm = this.colModel;
35569         this.cm.xmodule = this.xmodule || false;
35570     }
35571     if (this.dataSource) {
35572         this.dataSource= Roo.factory(this.dataSource, Roo.data);
35573         this.ds = this.dataSource;
35574         this.ds.xmodule = this.xmodule || false;
35575          
35576     }
35577     
35578     
35579     
35580     if(this.width){
35581         this.container.setWidth(this.width);
35582     }
35583
35584     if(this.height){
35585         this.container.setHeight(this.height);
35586     }
35587     /** @private */
35588         this.addEvents({
35589         // raw events
35590         /**
35591          * @event click
35592          * The raw click event for the entire grid.
35593          * @param {Roo.EventObject} e
35594          */
35595         "click" : true,
35596         /**
35597          * @event dblclick
35598          * The raw dblclick event for the entire grid.
35599          * @param {Roo.EventObject} e
35600          */
35601         "dblclick" : true,
35602         /**
35603          * @event contextmenu
35604          * The raw contextmenu event for the entire grid.
35605          * @param {Roo.EventObject} e
35606          */
35607         "contextmenu" : true,
35608         /**
35609          * @event mousedown
35610          * The raw mousedown event for the entire grid.
35611          * @param {Roo.EventObject} e
35612          */
35613         "mousedown" : true,
35614         /**
35615          * @event mouseup
35616          * The raw mouseup event for the entire grid.
35617          * @param {Roo.EventObject} e
35618          */
35619         "mouseup" : true,
35620         /**
35621          * @event mouseover
35622          * The raw mouseover event for the entire grid.
35623          * @param {Roo.EventObject} e
35624          */
35625         "mouseover" : true,
35626         /**
35627          * @event mouseout
35628          * The raw mouseout event for the entire grid.
35629          * @param {Roo.EventObject} e
35630          */
35631         "mouseout" : true,
35632         /**
35633          * @event keypress
35634          * The raw keypress event for the entire grid.
35635          * @param {Roo.EventObject} e
35636          */
35637         "keypress" : true,
35638         /**
35639          * @event keydown
35640          * The raw keydown event for the entire grid.
35641          * @param {Roo.EventObject} e
35642          */
35643         "keydown" : true,
35644
35645         // custom events
35646
35647         /**
35648          * @event cellclick
35649          * Fires when a cell is clicked
35650          * @param {Grid} this
35651          * @param {Number} rowIndex
35652          * @param {Number} columnIndex
35653          * @param {Roo.EventObject} e
35654          */
35655         "cellclick" : true,
35656         /**
35657          * @event celldblclick
35658          * Fires when a cell is double clicked
35659          * @param {Grid} this
35660          * @param {Number} rowIndex
35661          * @param {Number} columnIndex
35662          * @param {Roo.EventObject} e
35663          */
35664         "celldblclick" : true,
35665         /**
35666          * @event rowclick
35667          * Fires when a row is clicked
35668          * @param {Grid} this
35669          * @param {Number} rowIndex
35670          * @param {Roo.EventObject} e
35671          */
35672         "rowclick" : true,
35673         /**
35674          * @event rowdblclick
35675          * Fires when a row is double clicked
35676          * @param {Grid} this
35677          * @param {Number} rowIndex
35678          * @param {Roo.EventObject} e
35679          */
35680         "rowdblclick" : true,
35681         /**
35682          * @event headerclick
35683          * Fires when a header is clicked
35684          * @param {Grid} this
35685          * @param {Number} columnIndex
35686          * @param {Roo.EventObject} e
35687          */
35688         "headerclick" : true,
35689         /**
35690          * @event headerdblclick
35691          * Fires when a header cell is double clicked
35692          * @param {Grid} this
35693          * @param {Number} columnIndex
35694          * @param {Roo.EventObject} e
35695          */
35696         "headerdblclick" : true,
35697         /**
35698          * @event rowcontextmenu
35699          * Fires when a row is right clicked
35700          * @param {Grid} this
35701          * @param {Number} rowIndex
35702          * @param {Roo.EventObject} e
35703          */
35704         "rowcontextmenu" : true,
35705         /**
35706          * @event cellcontextmenu
35707          * Fires when a cell is right clicked
35708          * @param {Grid} this
35709          * @param {Number} rowIndex
35710          * @param {Number} cellIndex
35711          * @param {Roo.EventObject} e
35712          */
35713          "cellcontextmenu" : true,
35714         /**
35715          * @event headercontextmenu
35716          * Fires when a header is right clicked
35717          * @param {Grid} this
35718          * @param {Number} columnIndex
35719          * @param {Roo.EventObject} e
35720          */
35721         "headercontextmenu" : true,
35722         /**
35723          * @event bodyscroll
35724          * Fires when the body element is scrolled
35725          * @param {Number} scrollLeft
35726          * @param {Number} scrollTop
35727          */
35728         "bodyscroll" : true,
35729         /**
35730          * @event columnresize
35731          * Fires when the user resizes a column
35732          * @param {Number} columnIndex
35733          * @param {Number} newSize
35734          */
35735         "columnresize" : true,
35736         /**
35737          * @event columnmove
35738          * Fires when the user moves a column
35739          * @param {Number} oldIndex
35740          * @param {Number} newIndex
35741          */
35742         "columnmove" : true,
35743         /**
35744          * @event startdrag
35745          * Fires when row(s) start being dragged
35746          * @param {Grid} this
35747          * @param {Roo.GridDD} dd The drag drop object
35748          * @param {event} e The raw browser event
35749          */
35750         "startdrag" : true,
35751         /**
35752          * @event enddrag
35753          * Fires when a drag operation is complete
35754          * @param {Grid} this
35755          * @param {Roo.GridDD} dd The drag drop object
35756          * @param {event} e The raw browser event
35757          */
35758         "enddrag" : true,
35759         /**
35760          * @event dragdrop
35761          * Fires when dragged row(s) are dropped on a valid DD target
35762          * @param {Grid} this
35763          * @param {Roo.GridDD} dd The drag drop object
35764          * @param {String} targetId The target drag drop object
35765          * @param {event} e The raw browser event
35766          */
35767         "dragdrop" : true,
35768         /**
35769          * @event dragover
35770          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
35771          * @param {Grid} this
35772          * @param {Roo.GridDD} dd The drag drop object
35773          * @param {String} targetId The target drag drop object
35774          * @param {event} e The raw browser event
35775          */
35776         "dragover" : true,
35777         /**
35778          * @event dragenter
35779          *  Fires when the dragged row(s) first cross another DD target while being dragged
35780          * @param {Grid} this
35781          * @param {Roo.GridDD} dd The drag drop object
35782          * @param {String} targetId The target drag drop object
35783          * @param {event} e The raw browser event
35784          */
35785         "dragenter" : true,
35786         /**
35787          * @event dragout
35788          * Fires when the dragged row(s) leave another DD target while being dragged
35789          * @param {Grid} this
35790          * @param {Roo.GridDD} dd The drag drop object
35791          * @param {String} targetId The target drag drop object
35792          * @param {event} e The raw browser event
35793          */
35794         "dragout" : true,
35795         /**
35796          * @event rowclass
35797          * Fires when a row is rendered, so you can change add a style to it.
35798          * @param {GridView} gridview   The grid view
35799          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
35800          */
35801         'rowclass' : true,
35802
35803         /**
35804          * @event render
35805          * Fires when the grid is rendered
35806          * @param {Grid} grid
35807          */
35808         'render' : true
35809     });
35810
35811     Roo.grid.Grid.superclass.constructor.call(this);
35812 };
35813 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
35814     
35815     /**
35816      * @cfg {String} ddGroup - drag drop group.
35817      */
35818
35819     /**
35820      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
35821      */
35822     minColumnWidth : 25,
35823
35824     /**
35825      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
35826      * <b>on initial render.</b> It is more efficient to explicitly size the columns
35827      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
35828      */
35829     autoSizeColumns : false,
35830
35831     /**
35832      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
35833      */
35834     autoSizeHeaders : true,
35835
35836     /**
35837      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
35838      */
35839     monitorWindowResize : true,
35840
35841     /**
35842      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
35843      * rows measured to get a columns size. Default is 0 (all rows).
35844      */
35845     maxRowsToMeasure : 0,
35846
35847     /**
35848      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
35849      */
35850     trackMouseOver : true,
35851
35852     /**
35853     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
35854     */
35855     
35856     /**
35857     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
35858     */
35859     enableDragDrop : false,
35860     
35861     /**
35862     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
35863     */
35864     enableColumnMove : true,
35865     
35866     /**
35867     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
35868     */
35869     enableColumnHide : true,
35870     
35871     /**
35872     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
35873     */
35874     enableRowHeightSync : false,
35875     
35876     /**
35877     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
35878     */
35879     stripeRows : true,
35880     
35881     /**
35882     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
35883     */
35884     autoHeight : false,
35885
35886     /**
35887      * @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.
35888      */
35889     autoExpandColumn : false,
35890
35891     /**
35892     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
35893     * Default is 50.
35894     */
35895     autoExpandMin : 50,
35896
35897     /**
35898     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
35899     */
35900     autoExpandMax : 1000,
35901
35902     /**
35903     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
35904     */
35905     view : null,
35906
35907     /**
35908     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
35909     */
35910     loadMask : false,
35911     /**
35912     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
35913     */
35914     dropTarget: false,
35915     
35916    
35917     
35918     // private
35919     rendered : false,
35920
35921     /**
35922     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
35923     * of a fixed width. Default is false.
35924     */
35925     /**
35926     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
35927     */
35928     /**
35929      * Called once after all setup has been completed and the grid is ready to be rendered.
35930      * @return {Roo.grid.Grid} this
35931      */
35932     render : function()
35933     {
35934         var c = this.container;
35935         // try to detect autoHeight/width mode
35936         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
35937             this.autoHeight = true;
35938         }
35939         var view = this.getView();
35940         view.init(this);
35941
35942         c.on("click", this.onClick, this);
35943         c.on("dblclick", this.onDblClick, this);
35944         c.on("contextmenu", this.onContextMenu, this);
35945         c.on("keydown", this.onKeyDown, this);
35946         if (Roo.isTouch) {
35947             c.on("touchstart", this.onTouchStart, this);
35948         }
35949
35950         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
35951
35952         this.getSelectionModel().init(this);
35953
35954         view.render();
35955
35956         if(this.loadMask){
35957             this.loadMask = new Roo.LoadMask(this.container,
35958                     Roo.apply({store:this.dataSource}, this.loadMask));
35959         }
35960         
35961         
35962         if (this.toolbar && this.toolbar.xtype) {
35963             this.toolbar.container = this.getView().getHeaderPanel(true);
35964             this.toolbar = new Roo.Toolbar(this.toolbar);
35965         }
35966         if (this.footer && this.footer.xtype) {
35967             this.footer.dataSource = this.getDataSource();
35968             this.footer.container = this.getView().getFooterPanel(true);
35969             this.footer = Roo.factory(this.footer, Roo);
35970         }
35971         if (this.dropTarget && this.dropTarget.xtype) {
35972             delete this.dropTarget.xtype;
35973             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
35974         }
35975         
35976         
35977         this.rendered = true;
35978         this.fireEvent('render', this);
35979         return this;
35980     },
35981
35982         /**
35983          * Reconfigures the grid to use a different Store and Column Model.
35984          * The View will be bound to the new objects and refreshed.
35985          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
35986          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
35987          */
35988     reconfigure : function(dataSource, colModel){
35989         if(this.loadMask){
35990             this.loadMask.destroy();
35991             this.loadMask = new Roo.LoadMask(this.container,
35992                     Roo.apply({store:dataSource}, this.loadMask));
35993         }
35994         this.view.bind(dataSource, colModel);
35995         this.dataSource = dataSource;
35996         this.colModel = colModel;
35997         this.view.refresh(true);
35998     },
35999
36000     // private
36001     onKeyDown : function(e){
36002         this.fireEvent("keydown", e);
36003     },
36004
36005     /**
36006      * Destroy this grid.
36007      * @param {Boolean} removeEl True to remove the element
36008      */
36009     destroy : function(removeEl, keepListeners){
36010         if(this.loadMask){
36011             this.loadMask.destroy();
36012         }
36013         var c = this.container;
36014         c.removeAllListeners();
36015         this.view.destroy();
36016         this.colModel.purgeListeners();
36017         if(!keepListeners){
36018             this.purgeListeners();
36019         }
36020         c.update("");
36021         if(removeEl === true){
36022             c.remove();
36023         }
36024     },
36025
36026     // private
36027     processEvent : function(name, e){
36028         // does this fire select???
36029         Roo.log('grid:processEvent '  + name);
36030         
36031         if (name != 'touchstart' ) {
36032             this.fireEvent(name, e);    
36033         }
36034         
36035         var t = e.getTarget();
36036         var v = this.view;
36037         var header = v.findHeaderIndex(t);
36038         if(header !== false){
36039             var ename = name == 'touchstart' ? 'click' : name;
36040              
36041             this.fireEvent("header" + ename, this, header, e);
36042         }else{
36043             var row = v.findRowIndex(t);
36044             var cell = v.findCellIndex(t);
36045             if (name == 'touchstart') {
36046                 // first touch is always a click.
36047                 // hopefull this happens after selection is updated.?
36048                 name = false;
36049                 
36050                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
36051                     var cs = this.selModel.getSelectedCell();
36052                     if (row == cs[0] && cell == cs[1]){
36053                         name = 'dblclick';
36054                     }
36055                 }
36056                 if (typeof(this.selModel.getSelections) != 'undefined') {
36057                     var cs = this.selModel.getSelections();
36058                     var ds = this.dataSource;
36059                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
36060                         name = 'dblclick';
36061                     }
36062                 }
36063                 if (!name) {
36064                     return;
36065                 }
36066             }
36067             
36068             
36069             if(row !== false){
36070                 this.fireEvent("row" + name, this, row, e);
36071                 if(cell !== false){
36072                     this.fireEvent("cell" + name, this, row, cell, e);
36073                 }
36074             }
36075         }
36076     },
36077
36078     // private
36079     onClick : function(e){
36080         this.processEvent("click", e);
36081     },
36082    // private
36083     onTouchStart : function(e){
36084         this.processEvent("touchstart", e);
36085     },
36086
36087     // private
36088     onContextMenu : function(e, t){
36089         this.processEvent("contextmenu", e);
36090     },
36091
36092     // private
36093     onDblClick : function(e){
36094         this.processEvent("dblclick", e);
36095     },
36096
36097     // private
36098     walkCells : function(row, col, step, fn, scope){
36099         var cm = this.colModel, clen = cm.getColumnCount();
36100         var ds = this.dataSource, rlen = ds.getCount(), first = true;
36101         if(step < 0){
36102             if(col < 0){
36103                 row--;
36104                 first = false;
36105             }
36106             while(row >= 0){
36107                 if(!first){
36108                     col = clen-1;
36109                 }
36110                 first = false;
36111                 while(col >= 0){
36112                     if(fn.call(scope || this, row, col, cm) === true){
36113                         return [row, col];
36114                     }
36115                     col--;
36116                 }
36117                 row--;
36118             }
36119         } else {
36120             if(col >= clen){
36121                 row++;
36122                 first = false;
36123             }
36124             while(row < rlen){
36125                 if(!first){
36126                     col = 0;
36127                 }
36128                 first = false;
36129                 while(col < clen){
36130                     if(fn.call(scope || this, row, col, cm) === true){
36131                         return [row, col];
36132                     }
36133                     col++;
36134                 }
36135                 row++;
36136             }
36137         }
36138         return null;
36139     },
36140
36141     // private
36142     getSelections : function(){
36143         return this.selModel.getSelections();
36144     },
36145
36146     /**
36147      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
36148      * but if manual update is required this method will initiate it.
36149      */
36150     autoSize : function(){
36151         if(this.rendered){
36152             this.view.layout();
36153             if(this.view.adjustForScroll){
36154                 this.view.adjustForScroll();
36155             }
36156         }
36157     },
36158
36159     /**
36160      * Returns the grid's underlying element.
36161      * @return {Element} The element
36162      */
36163     getGridEl : function(){
36164         return this.container;
36165     },
36166
36167     // private for compatibility, overridden by editor grid
36168     stopEditing : function(){},
36169
36170     /**
36171      * Returns the grid's SelectionModel.
36172      * @return {SelectionModel}
36173      */
36174     getSelectionModel : function(){
36175         if(!this.selModel){
36176             this.selModel = new Roo.grid.RowSelectionModel();
36177         }
36178         return this.selModel;
36179     },
36180
36181     /**
36182      * Returns the grid's DataSource.
36183      * @return {DataSource}
36184      */
36185     getDataSource : function(){
36186         return this.dataSource;
36187     },
36188
36189     /**
36190      * Returns the grid's ColumnModel.
36191      * @return {ColumnModel}
36192      */
36193     getColumnModel : function(){
36194         return this.colModel;
36195     },
36196
36197     /**
36198      * Returns the grid's GridView object.
36199      * @return {GridView}
36200      */
36201     getView : function(){
36202         if(!this.view){
36203             this.view = new Roo.grid.GridView(this.viewConfig);
36204         }
36205         return this.view;
36206     },
36207     /**
36208      * Called to get grid's drag proxy text, by default returns this.ddText.
36209      * @return {String}
36210      */
36211     getDragDropText : function(){
36212         var count = this.selModel.getCount();
36213         return String.format(this.ddText, count, count == 1 ? '' : 's');
36214     }
36215 });
36216 /**
36217  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
36218  * %0 is replaced with the number of selected rows.
36219  * @type String
36220  */
36221 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
36222  * Based on:
36223  * Ext JS Library 1.1.1
36224  * Copyright(c) 2006-2007, Ext JS, LLC.
36225  *
36226  * Originally Released Under LGPL - original licence link has changed is not relivant.
36227  *
36228  * Fork - LGPL
36229  * <script type="text/javascript">
36230  */
36231  
36232 Roo.grid.AbstractGridView = function(){
36233         this.grid = null;
36234         
36235         this.events = {
36236             "beforerowremoved" : true,
36237             "beforerowsinserted" : true,
36238             "beforerefresh" : true,
36239             "rowremoved" : true,
36240             "rowsinserted" : true,
36241             "rowupdated" : true,
36242             "refresh" : true
36243         };
36244     Roo.grid.AbstractGridView.superclass.constructor.call(this);
36245 };
36246
36247 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
36248     rowClass : "x-grid-row",
36249     cellClass : "x-grid-cell",
36250     tdClass : "x-grid-td",
36251     hdClass : "x-grid-hd",
36252     splitClass : "x-grid-hd-split",
36253     
36254     init: function(grid){
36255         this.grid = grid;
36256                 var cid = this.grid.getGridEl().id;
36257         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
36258         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
36259         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
36260         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
36261         },
36262         
36263     getColumnRenderers : function(){
36264         var renderers = [];
36265         var cm = this.grid.colModel;
36266         var colCount = cm.getColumnCount();
36267         for(var i = 0; i < colCount; i++){
36268             renderers[i] = cm.getRenderer(i);
36269         }
36270         return renderers;
36271     },
36272     
36273     getColumnIds : function(){
36274         var ids = [];
36275         var cm = this.grid.colModel;
36276         var colCount = cm.getColumnCount();
36277         for(var i = 0; i < colCount; i++){
36278             ids[i] = cm.getColumnId(i);
36279         }
36280         return ids;
36281     },
36282     
36283     getDataIndexes : function(){
36284         if(!this.indexMap){
36285             this.indexMap = this.buildIndexMap();
36286         }
36287         return this.indexMap.colToData;
36288     },
36289     
36290     getColumnIndexByDataIndex : function(dataIndex){
36291         if(!this.indexMap){
36292             this.indexMap = this.buildIndexMap();
36293         }
36294         return this.indexMap.dataToCol[dataIndex];
36295     },
36296     
36297     /**
36298      * Set a css style for a column dynamically. 
36299      * @param {Number} colIndex The index of the column
36300      * @param {String} name The css property name
36301      * @param {String} value The css value
36302      */
36303     setCSSStyle : function(colIndex, name, value){
36304         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
36305         Roo.util.CSS.updateRule(selector, name, value);
36306     },
36307     
36308     generateRules : function(cm){
36309         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
36310         Roo.util.CSS.removeStyleSheet(rulesId);
36311         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36312             var cid = cm.getColumnId(i);
36313             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
36314                          this.tdSelector, cid, " {\n}\n",
36315                          this.hdSelector, cid, " {\n}\n",
36316                          this.splitSelector, cid, " {\n}\n");
36317         }
36318         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
36319     }
36320 });/*
36321  * Based on:
36322  * Ext JS Library 1.1.1
36323  * Copyright(c) 2006-2007, Ext JS, LLC.
36324  *
36325  * Originally Released Under LGPL - original licence link has changed is not relivant.
36326  *
36327  * Fork - LGPL
36328  * <script type="text/javascript">
36329  */
36330
36331 // private
36332 // This is a support class used internally by the Grid components
36333 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
36334     this.grid = grid;
36335     this.view = grid.getView();
36336     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36337     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
36338     if(hd2){
36339         this.setHandleElId(Roo.id(hd));
36340         this.setOuterHandleElId(Roo.id(hd2));
36341     }
36342     this.scroll = false;
36343 };
36344 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
36345     maxDragWidth: 120,
36346     getDragData : function(e){
36347         var t = Roo.lib.Event.getTarget(e);
36348         var h = this.view.findHeaderCell(t);
36349         if(h){
36350             return {ddel: h.firstChild, header:h};
36351         }
36352         return false;
36353     },
36354
36355     onInitDrag : function(e){
36356         this.view.headersDisabled = true;
36357         var clone = this.dragData.ddel.cloneNode(true);
36358         clone.id = Roo.id();
36359         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
36360         this.proxy.update(clone);
36361         return true;
36362     },
36363
36364     afterValidDrop : function(){
36365         var v = this.view;
36366         setTimeout(function(){
36367             v.headersDisabled = false;
36368         }, 50);
36369     },
36370
36371     afterInvalidDrop : function(){
36372         var v = this.view;
36373         setTimeout(function(){
36374             v.headersDisabled = false;
36375         }, 50);
36376     }
36377 });
36378 /*
36379  * Based on:
36380  * Ext JS Library 1.1.1
36381  * Copyright(c) 2006-2007, Ext JS, LLC.
36382  *
36383  * Originally Released Under LGPL - original licence link has changed is not relivant.
36384  *
36385  * Fork - LGPL
36386  * <script type="text/javascript">
36387  */
36388 // private
36389 // This is a support class used internally by the Grid components
36390 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
36391     this.grid = grid;
36392     this.view = grid.getView();
36393     // split the proxies so they don't interfere with mouse events
36394     this.proxyTop = Roo.DomHelper.append(document.body, {
36395         cls:"col-move-top", html:"&#160;"
36396     }, true);
36397     this.proxyBottom = Roo.DomHelper.append(document.body, {
36398         cls:"col-move-bottom", html:"&#160;"
36399     }, true);
36400     this.proxyTop.hide = this.proxyBottom.hide = function(){
36401         this.setLeftTop(-100,-100);
36402         this.setStyle("visibility", "hidden");
36403     };
36404     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36405     // temporarily disabled
36406     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
36407     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
36408 };
36409 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
36410     proxyOffsets : [-4, -9],
36411     fly: Roo.Element.fly,
36412
36413     getTargetFromEvent : function(e){
36414         var t = Roo.lib.Event.getTarget(e);
36415         var cindex = this.view.findCellIndex(t);
36416         if(cindex !== false){
36417             return this.view.getHeaderCell(cindex);
36418         }
36419         return null;
36420     },
36421
36422     nextVisible : function(h){
36423         var v = this.view, cm = this.grid.colModel;
36424         h = h.nextSibling;
36425         while(h){
36426             if(!cm.isHidden(v.getCellIndex(h))){
36427                 return h;
36428             }
36429             h = h.nextSibling;
36430         }
36431         return null;
36432     },
36433
36434     prevVisible : function(h){
36435         var v = this.view, cm = this.grid.colModel;
36436         h = h.prevSibling;
36437         while(h){
36438             if(!cm.isHidden(v.getCellIndex(h))){
36439                 return h;
36440             }
36441             h = h.prevSibling;
36442         }
36443         return null;
36444     },
36445
36446     positionIndicator : function(h, n, e){
36447         var x = Roo.lib.Event.getPageX(e);
36448         var r = Roo.lib.Dom.getRegion(n.firstChild);
36449         var px, pt, py = r.top + this.proxyOffsets[1];
36450         if((r.right - x) <= (r.right-r.left)/2){
36451             px = r.right+this.view.borderWidth;
36452             pt = "after";
36453         }else{
36454             px = r.left;
36455             pt = "before";
36456         }
36457         var oldIndex = this.view.getCellIndex(h);
36458         var newIndex = this.view.getCellIndex(n);
36459
36460         if(this.grid.colModel.isFixed(newIndex)){
36461             return false;
36462         }
36463
36464         var locked = this.grid.colModel.isLocked(newIndex);
36465
36466         if(pt == "after"){
36467             newIndex++;
36468         }
36469         if(oldIndex < newIndex){
36470             newIndex--;
36471         }
36472         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
36473             return false;
36474         }
36475         px +=  this.proxyOffsets[0];
36476         this.proxyTop.setLeftTop(px, py);
36477         this.proxyTop.show();
36478         if(!this.bottomOffset){
36479             this.bottomOffset = this.view.mainHd.getHeight();
36480         }
36481         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
36482         this.proxyBottom.show();
36483         return pt;
36484     },
36485
36486     onNodeEnter : function(n, dd, e, data){
36487         if(data.header != n){
36488             this.positionIndicator(data.header, n, e);
36489         }
36490     },
36491
36492     onNodeOver : function(n, dd, e, data){
36493         var result = false;
36494         if(data.header != n){
36495             result = this.positionIndicator(data.header, n, e);
36496         }
36497         if(!result){
36498             this.proxyTop.hide();
36499             this.proxyBottom.hide();
36500         }
36501         return result ? this.dropAllowed : this.dropNotAllowed;
36502     },
36503
36504     onNodeOut : function(n, dd, e, data){
36505         this.proxyTop.hide();
36506         this.proxyBottom.hide();
36507     },
36508
36509     onNodeDrop : function(n, dd, e, data){
36510         var h = data.header;
36511         if(h != n){
36512             var cm = this.grid.colModel;
36513             var x = Roo.lib.Event.getPageX(e);
36514             var r = Roo.lib.Dom.getRegion(n.firstChild);
36515             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
36516             var oldIndex = this.view.getCellIndex(h);
36517             var newIndex = this.view.getCellIndex(n);
36518             var locked = cm.isLocked(newIndex);
36519             if(pt == "after"){
36520                 newIndex++;
36521             }
36522             if(oldIndex < newIndex){
36523                 newIndex--;
36524             }
36525             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
36526                 return false;
36527             }
36528             cm.setLocked(oldIndex, locked, true);
36529             cm.moveColumn(oldIndex, newIndex);
36530             this.grid.fireEvent("columnmove", oldIndex, newIndex);
36531             return true;
36532         }
36533         return false;
36534     }
36535 });
36536 /*
36537  * Based on:
36538  * Ext JS Library 1.1.1
36539  * Copyright(c) 2006-2007, Ext JS, LLC.
36540  *
36541  * Originally Released Under LGPL - original licence link has changed is not relivant.
36542  *
36543  * Fork - LGPL
36544  * <script type="text/javascript">
36545  */
36546   
36547 /**
36548  * @class Roo.grid.GridView
36549  * @extends Roo.util.Observable
36550  *
36551  * @constructor
36552  * @param {Object} config
36553  */
36554 Roo.grid.GridView = function(config){
36555     Roo.grid.GridView.superclass.constructor.call(this);
36556     this.el = null;
36557
36558     Roo.apply(this, config);
36559 };
36560
36561 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
36562
36563     unselectable :  'unselectable="on"',
36564     unselectableCls :  'x-unselectable',
36565     
36566     
36567     rowClass : "x-grid-row",
36568
36569     cellClass : "x-grid-col",
36570
36571     tdClass : "x-grid-td",
36572
36573     hdClass : "x-grid-hd",
36574
36575     splitClass : "x-grid-split",
36576
36577     sortClasses : ["sort-asc", "sort-desc"],
36578
36579     enableMoveAnim : false,
36580
36581     hlColor: "C3DAF9",
36582
36583     dh : Roo.DomHelper,
36584
36585     fly : Roo.Element.fly,
36586
36587     css : Roo.util.CSS,
36588
36589     borderWidth: 1,
36590
36591     splitOffset: 3,
36592
36593     scrollIncrement : 22,
36594
36595     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
36596
36597     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
36598
36599     bind : function(ds, cm){
36600         if(this.ds){
36601             this.ds.un("load", this.onLoad, this);
36602             this.ds.un("datachanged", this.onDataChange, this);
36603             this.ds.un("add", this.onAdd, this);
36604             this.ds.un("remove", this.onRemove, this);
36605             this.ds.un("update", this.onUpdate, this);
36606             this.ds.un("clear", this.onClear, this);
36607         }
36608         if(ds){
36609             ds.on("load", this.onLoad, this);
36610             ds.on("datachanged", this.onDataChange, this);
36611             ds.on("add", this.onAdd, this);
36612             ds.on("remove", this.onRemove, this);
36613             ds.on("update", this.onUpdate, this);
36614             ds.on("clear", this.onClear, this);
36615         }
36616         this.ds = ds;
36617
36618         if(this.cm){
36619             this.cm.un("widthchange", this.onColWidthChange, this);
36620             this.cm.un("headerchange", this.onHeaderChange, this);
36621             this.cm.un("hiddenchange", this.onHiddenChange, this);
36622             this.cm.un("columnmoved", this.onColumnMove, this);
36623             this.cm.un("columnlockchange", this.onColumnLock, this);
36624         }
36625         if(cm){
36626             this.generateRules(cm);
36627             cm.on("widthchange", this.onColWidthChange, this);
36628             cm.on("headerchange", this.onHeaderChange, this);
36629             cm.on("hiddenchange", this.onHiddenChange, this);
36630             cm.on("columnmoved", this.onColumnMove, this);
36631             cm.on("columnlockchange", this.onColumnLock, this);
36632         }
36633         this.cm = cm;
36634     },
36635
36636     init: function(grid){
36637         Roo.grid.GridView.superclass.init.call(this, grid);
36638
36639         this.bind(grid.dataSource, grid.colModel);
36640
36641         grid.on("headerclick", this.handleHeaderClick, this);
36642
36643         if(grid.trackMouseOver){
36644             grid.on("mouseover", this.onRowOver, this);
36645             grid.on("mouseout", this.onRowOut, this);
36646         }
36647         grid.cancelTextSelection = function(){};
36648         this.gridId = grid.id;
36649
36650         var tpls = this.templates || {};
36651
36652         if(!tpls.master){
36653             tpls.master = new Roo.Template(
36654                '<div class="x-grid" hidefocus="true">',
36655                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
36656                   '<div class="x-grid-topbar"></div>',
36657                   '<div class="x-grid-scroller"><div></div></div>',
36658                   '<div class="x-grid-locked">',
36659                       '<div class="x-grid-header">{lockedHeader}</div>',
36660                       '<div class="x-grid-body">{lockedBody}</div>',
36661                   "</div>",
36662                   '<div class="x-grid-viewport">',
36663                       '<div class="x-grid-header">{header}</div>',
36664                       '<div class="x-grid-body">{body}</div>',
36665                   "</div>",
36666                   '<div class="x-grid-bottombar"></div>',
36667                  
36668                   '<div class="x-grid-resize-proxy">&#160;</div>',
36669                "</div>"
36670             );
36671             tpls.master.disableformats = true;
36672         }
36673
36674         if(!tpls.header){
36675             tpls.header = new Roo.Template(
36676                '<table border="0" cellspacing="0" cellpadding="0">',
36677                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
36678                "</table>{splits}"
36679             );
36680             tpls.header.disableformats = true;
36681         }
36682         tpls.header.compile();
36683
36684         if(!tpls.hcell){
36685             tpls.hcell = new Roo.Template(
36686                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
36687                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
36688                 "</div></td>"
36689              );
36690              tpls.hcell.disableFormats = true;
36691         }
36692         tpls.hcell.compile();
36693
36694         if(!tpls.hsplit){
36695             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
36696                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
36697             tpls.hsplit.disableFormats = true;
36698         }
36699         tpls.hsplit.compile();
36700
36701         if(!tpls.body){
36702             tpls.body = new Roo.Template(
36703                '<table border="0" cellspacing="0" cellpadding="0">',
36704                "<tbody>{rows}</tbody>",
36705                "</table>"
36706             );
36707             tpls.body.disableFormats = true;
36708         }
36709         tpls.body.compile();
36710
36711         if(!tpls.row){
36712             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
36713             tpls.row.disableFormats = true;
36714         }
36715         tpls.row.compile();
36716
36717         if(!tpls.cell){
36718             tpls.cell = new Roo.Template(
36719                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
36720                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
36721                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
36722                 "</td>"
36723             );
36724             tpls.cell.disableFormats = true;
36725         }
36726         tpls.cell.compile();
36727
36728         this.templates = tpls;
36729     },
36730
36731     // remap these for backwards compat
36732     onColWidthChange : function(){
36733         this.updateColumns.apply(this, arguments);
36734     },
36735     onHeaderChange : function(){
36736         this.updateHeaders.apply(this, arguments);
36737     }, 
36738     onHiddenChange : function(){
36739         this.handleHiddenChange.apply(this, arguments);
36740     },
36741     onColumnMove : function(){
36742         this.handleColumnMove.apply(this, arguments);
36743     },
36744     onColumnLock : function(){
36745         this.handleLockChange.apply(this, arguments);
36746     },
36747
36748     onDataChange : function(){
36749         this.refresh();
36750         this.updateHeaderSortState();
36751     },
36752
36753     onClear : function(){
36754         this.refresh();
36755     },
36756
36757     onUpdate : function(ds, record){
36758         this.refreshRow(record);
36759     },
36760
36761     refreshRow : function(record){
36762         var ds = this.ds, index;
36763         if(typeof record == 'number'){
36764             index = record;
36765             record = ds.getAt(index);
36766         }else{
36767             index = ds.indexOf(record);
36768         }
36769         this.insertRows(ds, index, index, true);
36770         this.onRemove(ds, record, index+1, true);
36771         this.syncRowHeights(index, index);
36772         this.layout();
36773         this.fireEvent("rowupdated", this, index, record);
36774     },
36775
36776     onAdd : function(ds, records, index){
36777         this.insertRows(ds, index, index + (records.length-1));
36778     },
36779
36780     onRemove : function(ds, record, index, isUpdate){
36781         if(isUpdate !== true){
36782             this.fireEvent("beforerowremoved", this, index, record);
36783         }
36784         var bt = this.getBodyTable(), lt = this.getLockedTable();
36785         if(bt.rows[index]){
36786             bt.firstChild.removeChild(bt.rows[index]);
36787         }
36788         if(lt.rows[index]){
36789             lt.firstChild.removeChild(lt.rows[index]);
36790         }
36791         if(isUpdate !== true){
36792             this.stripeRows(index);
36793             this.syncRowHeights(index, index);
36794             this.layout();
36795             this.fireEvent("rowremoved", this, index, record);
36796         }
36797     },
36798
36799     onLoad : function(){
36800         this.scrollToTop();
36801     },
36802
36803     /**
36804      * Scrolls the grid to the top
36805      */
36806     scrollToTop : function(){
36807         if(this.scroller){
36808             this.scroller.dom.scrollTop = 0;
36809             this.syncScroll();
36810         }
36811     },
36812
36813     /**
36814      * Gets a panel in the header of the grid that can be used for toolbars etc.
36815      * After modifying the contents of this panel a call to grid.autoSize() may be
36816      * required to register any changes in size.
36817      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
36818      * @return Roo.Element
36819      */
36820     getHeaderPanel : function(doShow){
36821         if(doShow){
36822             this.headerPanel.show();
36823         }
36824         return this.headerPanel;
36825     },
36826
36827     /**
36828      * Gets a panel in the footer of the grid that can be used for toolbars etc.
36829      * After modifying the contents of this panel a call to grid.autoSize() may be
36830      * required to register any changes in size.
36831      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
36832      * @return Roo.Element
36833      */
36834     getFooterPanel : function(doShow){
36835         if(doShow){
36836             this.footerPanel.show();
36837         }
36838         return this.footerPanel;
36839     },
36840
36841     initElements : function(){
36842         var E = Roo.Element;
36843         var el = this.grid.getGridEl().dom.firstChild;
36844         var cs = el.childNodes;
36845
36846         this.el = new E(el);
36847         
36848          this.focusEl = new E(el.firstChild);
36849         this.focusEl.swallowEvent("click", true);
36850         
36851         this.headerPanel = new E(cs[1]);
36852         this.headerPanel.enableDisplayMode("block");
36853
36854         this.scroller = new E(cs[2]);
36855         this.scrollSizer = new E(this.scroller.dom.firstChild);
36856
36857         this.lockedWrap = new E(cs[3]);
36858         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
36859         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
36860
36861         this.mainWrap = new E(cs[4]);
36862         this.mainHd = new E(this.mainWrap.dom.firstChild);
36863         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
36864
36865         this.footerPanel = new E(cs[5]);
36866         this.footerPanel.enableDisplayMode("block");
36867
36868         this.resizeProxy = new E(cs[6]);
36869
36870         this.headerSelector = String.format(
36871            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
36872            this.lockedHd.id, this.mainHd.id
36873         );
36874
36875         this.splitterSelector = String.format(
36876            '#{0} div.x-grid-split, #{1} div.x-grid-split',
36877            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
36878         );
36879     },
36880     idToCssName : function(s)
36881     {
36882         return s.replace(/[^a-z0-9]+/ig, '-');
36883     },
36884
36885     getHeaderCell : function(index){
36886         return Roo.DomQuery.select(this.headerSelector)[index];
36887     },
36888
36889     getHeaderCellMeasure : function(index){
36890         return this.getHeaderCell(index).firstChild;
36891     },
36892
36893     getHeaderCellText : function(index){
36894         return this.getHeaderCell(index).firstChild.firstChild;
36895     },
36896
36897     getLockedTable : function(){
36898         return this.lockedBody.dom.firstChild;
36899     },
36900
36901     getBodyTable : function(){
36902         return this.mainBody.dom.firstChild;
36903     },
36904
36905     getLockedRow : function(index){
36906         return this.getLockedTable().rows[index];
36907     },
36908
36909     getRow : function(index){
36910         return this.getBodyTable().rows[index];
36911     },
36912
36913     getRowComposite : function(index){
36914         if(!this.rowEl){
36915             this.rowEl = new Roo.CompositeElementLite();
36916         }
36917         var els = [], lrow, mrow;
36918         if(lrow = this.getLockedRow(index)){
36919             els.push(lrow);
36920         }
36921         if(mrow = this.getRow(index)){
36922             els.push(mrow);
36923         }
36924         this.rowEl.elements = els;
36925         return this.rowEl;
36926     },
36927     /**
36928      * Gets the 'td' of the cell
36929      * 
36930      * @param {Integer} rowIndex row to select
36931      * @param {Integer} colIndex column to select
36932      * 
36933      * @return {Object} 
36934      */
36935     getCell : function(rowIndex, colIndex){
36936         var locked = this.cm.getLockedCount();
36937         var source;
36938         if(colIndex < locked){
36939             source = this.lockedBody.dom.firstChild;
36940         }else{
36941             source = this.mainBody.dom.firstChild;
36942             colIndex -= locked;
36943         }
36944         return source.rows[rowIndex].childNodes[colIndex];
36945     },
36946
36947     getCellText : function(rowIndex, colIndex){
36948         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
36949     },
36950
36951     getCellBox : function(cell){
36952         var b = this.fly(cell).getBox();
36953         if(Roo.isOpera){ // opera fails to report the Y
36954             b.y = cell.offsetTop + this.mainBody.getY();
36955         }
36956         return b;
36957     },
36958
36959     getCellIndex : function(cell){
36960         var id = String(cell.className).match(this.cellRE);
36961         if(id){
36962             return parseInt(id[1], 10);
36963         }
36964         return 0;
36965     },
36966
36967     findHeaderIndex : function(n){
36968         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36969         return r ? this.getCellIndex(r) : false;
36970     },
36971
36972     findHeaderCell : function(n){
36973         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36974         return r ? r : false;
36975     },
36976
36977     findRowIndex : function(n){
36978         if(!n){
36979             return false;
36980         }
36981         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
36982         return r ? r.rowIndex : false;
36983     },
36984
36985     findCellIndex : function(node){
36986         var stop = this.el.dom;
36987         while(node && node != stop){
36988             if(this.findRE.test(node.className)){
36989                 return this.getCellIndex(node);
36990             }
36991             node = node.parentNode;
36992         }
36993         return false;
36994     },
36995
36996     getColumnId : function(index){
36997         return this.cm.getColumnId(index);
36998     },
36999
37000     getSplitters : function()
37001     {
37002         if(this.splitterSelector){
37003            return Roo.DomQuery.select(this.splitterSelector);
37004         }else{
37005             return null;
37006       }
37007     },
37008
37009     getSplitter : function(index){
37010         return this.getSplitters()[index];
37011     },
37012
37013     onRowOver : function(e, t){
37014         var row;
37015         if((row = this.findRowIndex(t)) !== false){
37016             this.getRowComposite(row).addClass("x-grid-row-over");
37017         }
37018     },
37019
37020     onRowOut : function(e, t){
37021         var row;
37022         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
37023             this.getRowComposite(row).removeClass("x-grid-row-over");
37024         }
37025     },
37026
37027     renderHeaders : function(){
37028         var cm = this.cm;
37029         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
37030         var cb = [], lb = [], sb = [], lsb = [], p = {};
37031         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37032             p.cellId = "x-grid-hd-0-" + i;
37033             p.splitId = "x-grid-csplit-0-" + i;
37034             p.id = cm.getColumnId(i);
37035             p.title = cm.getColumnTooltip(i) || "";
37036             p.value = cm.getColumnHeader(i) || "";
37037             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
37038             if(!cm.isLocked(i)){
37039                 cb[cb.length] = ct.apply(p);
37040                 sb[sb.length] = st.apply(p);
37041             }else{
37042                 lb[lb.length] = ct.apply(p);
37043                 lsb[lsb.length] = st.apply(p);
37044             }
37045         }
37046         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
37047                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
37048     },
37049
37050     updateHeaders : function(){
37051         var html = this.renderHeaders();
37052         this.lockedHd.update(html[0]);
37053         this.mainHd.update(html[1]);
37054     },
37055
37056     /**
37057      * Focuses the specified row.
37058      * @param {Number} row The row index
37059      */
37060     focusRow : function(row)
37061     {
37062         //Roo.log('GridView.focusRow');
37063         var x = this.scroller.dom.scrollLeft;
37064         this.focusCell(row, 0, false);
37065         this.scroller.dom.scrollLeft = x;
37066     },
37067
37068     /**
37069      * Focuses the specified cell.
37070      * @param {Number} row The row index
37071      * @param {Number} col The column index
37072      * @param {Boolean} hscroll false to disable horizontal scrolling
37073      */
37074     focusCell : function(row, col, hscroll)
37075     {
37076         //Roo.log('GridView.focusCell');
37077         var el = this.ensureVisible(row, col, hscroll);
37078         this.focusEl.alignTo(el, "tl-tl");
37079         if(Roo.isGecko){
37080             this.focusEl.focus();
37081         }else{
37082             this.focusEl.focus.defer(1, this.focusEl);
37083         }
37084     },
37085
37086     /**
37087      * Scrolls the specified cell into view
37088      * @param {Number} row The row index
37089      * @param {Number} col The column index
37090      * @param {Boolean} hscroll false to disable horizontal scrolling
37091      */
37092     ensureVisible : function(row, col, hscroll)
37093     {
37094         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
37095         //return null; //disable for testing.
37096         if(typeof row != "number"){
37097             row = row.rowIndex;
37098         }
37099         if(row < 0 && row >= this.ds.getCount()){
37100             return  null;
37101         }
37102         col = (col !== undefined ? col : 0);
37103         var cm = this.grid.colModel;
37104         while(cm.isHidden(col)){
37105             col++;
37106         }
37107
37108         var el = this.getCell(row, col);
37109         if(!el){
37110             return null;
37111         }
37112         var c = this.scroller.dom;
37113
37114         var ctop = parseInt(el.offsetTop, 10);
37115         var cleft = parseInt(el.offsetLeft, 10);
37116         var cbot = ctop + el.offsetHeight;
37117         var cright = cleft + el.offsetWidth;
37118         
37119         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
37120         var stop = parseInt(c.scrollTop, 10);
37121         var sleft = parseInt(c.scrollLeft, 10);
37122         var sbot = stop + ch;
37123         var sright = sleft + c.clientWidth;
37124         /*
37125         Roo.log('GridView.ensureVisible:' +
37126                 ' ctop:' + ctop +
37127                 ' c.clientHeight:' + c.clientHeight +
37128                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
37129                 ' stop:' + stop +
37130                 ' cbot:' + cbot +
37131                 ' sbot:' + sbot +
37132                 ' ch:' + ch  
37133                 );
37134         */
37135         if(ctop < stop){
37136              c.scrollTop = ctop;
37137             //Roo.log("set scrolltop to ctop DISABLE?");
37138         }else if(cbot > sbot){
37139             //Roo.log("set scrolltop to cbot-ch");
37140             c.scrollTop = cbot-ch;
37141         }
37142         
37143         if(hscroll !== false){
37144             if(cleft < sleft){
37145                 c.scrollLeft = cleft;
37146             }else if(cright > sright){
37147                 c.scrollLeft = cright-c.clientWidth;
37148             }
37149         }
37150          
37151         return el;
37152     },
37153
37154     updateColumns : function(){
37155         this.grid.stopEditing();
37156         var cm = this.grid.colModel, colIds = this.getColumnIds();
37157         //var totalWidth = cm.getTotalWidth();
37158         var pos = 0;
37159         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37160             //if(cm.isHidden(i)) continue;
37161             var w = cm.getColumnWidth(i);
37162             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37163             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37164         }
37165         this.updateSplitters();
37166     },
37167
37168     generateRules : function(cm){
37169         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
37170         Roo.util.CSS.removeStyleSheet(rulesId);
37171         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37172             var cid = cm.getColumnId(i);
37173             var align = '';
37174             if(cm.config[i].align){
37175                 align = 'text-align:'+cm.config[i].align+';';
37176             }
37177             var hidden = '';
37178             if(cm.isHidden(i)){
37179                 hidden = 'display:none;';
37180             }
37181             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
37182             ruleBuf.push(
37183                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
37184                     this.hdSelector, cid, " {\n", align, width, "}\n",
37185                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
37186                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
37187         }
37188         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37189     },
37190
37191     updateSplitters : function(){
37192         var cm = this.cm, s = this.getSplitters();
37193         if(s){ // splitters not created yet
37194             var pos = 0, locked = true;
37195             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37196                 if(cm.isHidden(i)) continue;
37197                 var w = cm.getColumnWidth(i); // make sure it's a number
37198                 if(!cm.isLocked(i) && locked){
37199                     pos = 0;
37200                     locked = false;
37201                 }
37202                 pos += w;
37203                 s[i].style.left = (pos-this.splitOffset) + "px";
37204             }
37205         }
37206     },
37207
37208     handleHiddenChange : function(colModel, colIndex, hidden){
37209         if(hidden){
37210             this.hideColumn(colIndex);
37211         }else{
37212             this.unhideColumn(colIndex);
37213         }
37214     },
37215
37216     hideColumn : function(colIndex){
37217         var cid = this.getColumnId(colIndex);
37218         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
37219         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
37220         if(Roo.isSafari){
37221             this.updateHeaders();
37222         }
37223         this.updateSplitters();
37224         this.layout();
37225     },
37226
37227     unhideColumn : function(colIndex){
37228         var cid = this.getColumnId(colIndex);
37229         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
37230         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
37231
37232         if(Roo.isSafari){
37233             this.updateHeaders();
37234         }
37235         this.updateSplitters();
37236         this.layout();
37237     },
37238
37239     insertRows : function(dm, firstRow, lastRow, isUpdate){
37240         if(firstRow == 0 && lastRow == dm.getCount()-1){
37241             this.refresh();
37242         }else{
37243             if(!isUpdate){
37244                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
37245             }
37246             var s = this.getScrollState();
37247             var markup = this.renderRows(firstRow, lastRow);
37248             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
37249             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
37250             this.restoreScroll(s);
37251             if(!isUpdate){
37252                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
37253                 this.syncRowHeights(firstRow, lastRow);
37254                 this.stripeRows(firstRow);
37255                 this.layout();
37256             }
37257         }
37258     },
37259
37260     bufferRows : function(markup, target, index){
37261         var before = null, trows = target.rows, tbody = target.tBodies[0];
37262         if(index < trows.length){
37263             before = trows[index];
37264         }
37265         var b = document.createElement("div");
37266         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
37267         var rows = b.firstChild.rows;
37268         for(var i = 0, len = rows.length; i < len; i++){
37269             if(before){
37270                 tbody.insertBefore(rows[0], before);
37271             }else{
37272                 tbody.appendChild(rows[0]);
37273             }
37274         }
37275         b.innerHTML = "";
37276         b = null;
37277     },
37278
37279     deleteRows : function(dm, firstRow, lastRow){
37280         if(dm.getRowCount()<1){
37281             this.fireEvent("beforerefresh", this);
37282             this.mainBody.update("");
37283             this.lockedBody.update("");
37284             this.fireEvent("refresh", this);
37285         }else{
37286             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
37287             var bt = this.getBodyTable();
37288             var tbody = bt.firstChild;
37289             var rows = bt.rows;
37290             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
37291                 tbody.removeChild(rows[firstRow]);
37292             }
37293             this.stripeRows(firstRow);
37294             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
37295         }
37296     },
37297
37298     updateRows : function(dataSource, firstRow, lastRow){
37299         var s = this.getScrollState();
37300         this.refresh();
37301         this.restoreScroll(s);
37302     },
37303
37304     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
37305         if(!noRefresh){
37306            this.refresh();
37307         }
37308         this.updateHeaderSortState();
37309     },
37310
37311     getScrollState : function(){
37312         
37313         var sb = this.scroller.dom;
37314         return {left: sb.scrollLeft, top: sb.scrollTop};
37315     },
37316
37317     stripeRows : function(startRow){
37318         if(!this.grid.stripeRows || this.ds.getCount() < 1){
37319             return;
37320         }
37321         startRow = startRow || 0;
37322         var rows = this.getBodyTable().rows;
37323         var lrows = this.getLockedTable().rows;
37324         var cls = ' x-grid-row-alt ';
37325         for(var i = startRow, len = rows.length; i < len; i++){
37326             var row = rows[i], lrow = lrows[i];
37327             var isAlt = ((i+1) % 2 == 0);
37328             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
37329             if(isAlt == hasAlt){
37330                 continue;
37331             }
37332             if(isAlt){
37333                 row.className += " x-grid-row-alt";
37334             }else{
37335                 row.className = row.className.replace("x-grid-row-alt", "");
37336             }
37337             if(lrow){
37338                 lrow.className = row.className;
37339             }
37340         }
37341     },
37342
37343     restoreScroll : function(state){
37344         //Roo.log('GridView.restoreScroll');
37345         var sb = this.scroller.dom;
37346         sb.scrollLeft = state.left;
37347         sb.scrollTop = state.top;
37348         this.syncScroll();
37349     },
37350
37351     syncScroll : function(){
37352         //Roo.log('GridView.syncScroll');
37353         var sb = this.scroller.dom;
37354         var sh = this.mainHd.dom;
37355         var bs = this.mainBody.dom;
37356         var lv = this.lockedBody.dom;
37357         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
37358         lv.scrollTop = bs.scrollTop = sb.scrollTop;
37359     },
37360
37361     handleScroll : function(e){
37362         this.syncScroll();
37363         var sb = this.scroller.dom;
37364         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
37365         e.stopEvent();
37366     },
37367
37368     handleWheel : function(e){
37369         var d = e.getWheelDelta();
37370         this.scroller.dom.scrollTop -= d*22;
37371         // set this here to prevent jumpy scrolling on large tables
37372         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
37373         e.stopEvent();
37374     },
37375
37376     renderRows : function(startRow, endRow){
37377         // pull in all the crap needed to render rows
37378         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
37379         var colCount = cm.getColumnCount();
37380
37381         if(ds.getCount() < 1){
37382             return ["", ""];
37383         }
37384
37385         // build a map for all the columns
37386         var cs = [];
37387         for(var i = 0; i < colCount; i++){
37388             var name = cm.getDataIndex(i);
37389             cs[i] = {
37390                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
37391                 renderer : cm.getRenderer(i),
37392                 id : cm.getColumnId(i),
37393                 locked : cm.isLocked(i)
37394             };
37395         }
37396
37397         startRow = startRow || 0;
37398         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
37399
37400         // records to render
37401         var rs = ds.getRange(startRow, endRow);
37402
37403         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
37404     },
37405
37406     // As much as I hate to duplicate code, this was branched because FireFox really hates
37407     // [].join("") on strings. The performance difference was substantial enough to
37408     // branch this function
37409     doRender : Roo.isGecko ?
37410             function(cs, rs, ds, startRow, colCount, stripe){
37411                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37412                 // buffers
37413                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37414                 
37415                 var hasListener = this.grid.hasListener('rowclass');
37416                 var rowcfg = {};
37417                 for(var j = 0, len = rs.length; j < len; j++){
37418                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
37419                     for(var i = 0; i < colCount; i++){
37420                         c = cs[i];
37421                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37422                         p.id = c.id;
37423                         p.css = p.attr = "";
37424                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37425                         if(p.value == undefined || p.value === "") p.value = "&#160;";
37426                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37427                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37428                         }
37429                         var markup = ct.apply(p);
37430                         if(!c.locked){
37431                             cb+= markup;
37432                         }else{
37433                             lcb+= markup;
37434                         }
37435                     }
37436                     var alt = [];
37437                     if(stripe && ((rowIndex+1) % 2 == 0)){
37438                         alt.push("x-grid-row-alt")
37439                     }
37440                     if(r.dirty){
37441                         alt.push(  " x-grid-dirty-row");
37442                     }
37443                     rp.cells = lcb;
37444                     if(this.getRowClass){
37445                         alt.push(this.getRowClass(r, rowIndex));
37446                     }
37447                     if (hasListener) {
37448                         rowcfg = {
37449                              
37450                             record: r,
37451                             rowIndex : rowIndex,
37452                             rowClass : ''
37453                         }
37454                         this.grid.fireEvent('rowclass', this, rowcfg);
37455                         alt.push(rowcfg.rowClass);
37456                     }
37457                     rp.alt = alt.join(" ");
37458                     lbuf+= rt.apply(rp);
37459                     rp.cells = cb;
37460                     buf+=  rt.apply(rp);
37461                 }
37462                 return [lbuf, buf];
37463             } :
37464             function(cs, rs, ds, startRow, colCount, stripe){
37465                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37466                 // buffers
37467                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37468                 var hasListener = this.grid.hasListener('rowclass');
37469  
37470                 var rowcfg = {};
37471                 for(var j = 0, len = rs.length; j < len; j++){
37472                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
37473                     for(var i = 0; i < colCount; i++){
37474                         c = cs[i];
37475                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37476                         p.id = c.id;
37477                         p.css = p.attr = "";
37478                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37479                         if(p.value == undefined || p.value === "") p.value = "&#160;";
37480                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37481                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37482                         }
37483                         
37484                         var markup = ct.apply(p);
37485                         if(!c.locked){
37486                             cb[cb.length] = markup;
37487                         }else{
37488                             lcb[lcb.length] = markup;
37489                         }
37490                     }
37491                     var alt = [];
37492                     if(stripe && ((rowIndex+1) % 2 == 0)){
37493                         alt.push( "x-grid-row-alt");
37494                     }
37495                     if(r.dirty){
37496                         alt.push(" x-grid-dirty-row");
37497                     }
37498                     rp.cells = lcb;
37499                     if(this.getRowClass){
37500                         alt.push( this.getRowClass(r, rowIndex));
37501                     }
37502                     if (hasListener) {
37503                         rowcfg = {
37504                              
37505                             record: r,
37506                             rowIndex : rowIndex,
37507                             rowClass : ''
37508                         }
37509                         this.grid.fireEvent('rowclass', this, rowcfg);
37510                         alt.push(rowcfg.rowClass);
37511                     }
37512                     rp.alt = alt.join(" ");
37513                     rp.cells = lcb.join("");
37514                     lbuf[lbuf.length] = rt.apply(rp);
37515                     rp.cells = cb.join("");
37516                     buf[buf.length] =  rt.apply(rp);
37517                 }
37518                 return [lbuf.join(""), buf.join("")];
37519             },
37520
37521     renderBody : function(){
37522         var markup = this.renderRows();
37523         var bt = this.templates.body;
37524         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
37525     },
37526
37527     /**
37528      * Refreshes the grid
37529      * @param {Boolean} headersToo
37530      */
37531     refresh : function(headersToo){
37532         this.fireEvent("beforerefresh", this);
37533         this.grid.stopEditing();
37534         var result = this.renderBody();
37535         this.lockedBody.update(result[0]);
37536         this.mainBody.update(result[1]);
37537         if(headersToo === true){
37538             this.updateHeaders();
37539             this.updateColumns();
37540             this.updateSplitters();
37541             this.updateHeaderSortState();
37542         }
37543         this.syncRowHeights();
37544         this.layout();
37545         this.fireEvent("refresh", this);
37546     },
37547
37548     handleColumnMove : function(cm, oldIndex, newIndex){
37549         this.indexMap = null;
37550         var s = this.getScrollState();
37551         this.refresh(true);
37552         this.restoreScroll(s);
37553         this.afterMove(newIndex);
37554     },
37555
37556     afterMove : function(colIndex){
37557         if(this.enableMoveAnim && Roo.enableFx){
37558             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
37559         }
37560         // if multisort - fix sortOrder, and reload..
37561         if (this.grid.dataSource.multiSort) {
37562             // the we can call sort again..
37563             var dm = this.grid.dataSource;
37564             var cm = this.grid.colModel;
37565             var so = [];
37566             for(var i = 0; i < cm.config.length; i++ ) {
37567                 
37568                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
37569                     continue; // dont' bother, it's not in sort list or being set.
37570                 }
37571                 
37572                 so.push(cm.config[i].dataIndex);
37573             };
37574             dm.sortOrder = so;
37575             dm.load(dm.lastOptions);
37576             
37577             
37578         }
37579         
37580     },
37581
37582     updateCell : function(dm, rowIndex, dataIndex){
37583         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
37584         if(typeof colIndex == "undefined"){ // not present in grid
37585             return;
37586         }
37587         var cm = this.grid.colModel;
37588         var cell = this.getCell(rowIndex, colIndex);
37589         var cellText = this.getCellText(rowIndex, colIndex);
37590
37591         var p = {
37592             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
37593             id : cm.getColumnId(colIndex),
37594             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
37595         };
37596         var renderer = cm.getRenderer(colIndex);
37597         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
37598         if(typeof val == "undefined" || val === "") val = "&#160;";
37599         cellText.innerHTML = val;
37600         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
37601         this.syncRowHeights(rowIndex, rowIndex);
37602     },
37603
37604     calcColumnWidth : function(colIndex, maxRowsToMeasure){
37605         var maxWidth = 0;
37606         if(this.grid.autoSizeHeaders){
37607             var h = this.getHeaderCellMeasure(colIndex);
37608             maxWidth = Math.max(maxWidth, h.scrollWidth);
37609         }
37610         var tb, index;
37611         if(this.cm.isLocked(colIndex)){
37612             tb = this.getLockedTable();
37613             index = colIndex;
37614         }else{
37615             tb = this.getBodyTable();
37616             index = colIndex - this.cm.getLockedCount();
37617         }
37618         if(tb && tb.rows){
37619             var rows = tb.rows;
37620             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
37621             for(var i = 0; i < stopIndex; i++){
37622                 var cell = rows[i].childNodes[index].firstChild;
37623                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
37624             }
37625         }
37626         return maxWidth + /*margin for error in IE*/ 5;
37627     },
37628     /**
37629      * Autofit a column to its content.
37630      * @param {Number} colIndex
37631      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
37632      */
37633      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
37634          if(this.cm.isHidden(colIndex)){
37635              return; // can't calc a hidden column
37636          }
37637         if(forceMinSize){
37638             var cid = this.cm.getColumnId(colIndex);
37639             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
37640            if(this.grid.autoSizeHeaders){
37641                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
37642            }
37643         }
37644         var newWidth = this.calcColumnWidth(colIndex);
37645         this.cm.setColumnWidth(colIndex,
37646             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
37647         if(!suppressEvent){
37648             this.grid.fireEvent("columnresize", colIndex, newWidth);
37649         }
37650     },
37651
37652     /**
37653      * Autofits all columns to their content and then expands to fit any extra space in the grid
37654      */
37655      autoSizeColumns : function(){
37656         var cm = this.grid.colModel;
37657         var colCount = cm.getColumnCount();
37658         for(var i = 0; i < colCount; i++){
37659             this.autoSizeColumn(i, true, true);
37660         }
37661         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
37662             this.fitColumns();
37663         }else{
37664             this.updateColumns();
37665             this.layout();
37666         }
37667     },
37668
37669     /**
37670      * Autofits all columns to the grid's width proportionate with their current size
37671      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
37672      */
37673     fitColumns : function(reserveScrollSpace){
37674         var cm = this.grid.colModel;
37675         var colCount = cm.getColumnCount();
37676         var cols = [];
37677         var width = 0;
37678         var i, w;
37679         for (i = 0; i < colCount; i++){
37680             if(!cm.isHidden(i) && !cm.isFixed(i)){
37681                 w = cm.getColumnWidth(i);
37682                 cols.push(i);
37683                 cols.push(w);
37684                 width += w;
37685             }
37686         }
37687         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
37688         if(reserveScrollSpace){
37689             avail -= 17;
37690         }
37691         var frac = (avail - cm.getTotalWidth())/width;
37692         while (cols.length){
37693             w = cols.pop();
37694             i = cols.pop();
37695             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
37696         }
37697         this.updateColumns();
37698         this.layout();
37699     },
37700
37701     onRowSelect : function(rowIndex){
37702         var row = this.getRowComposite(rowIndex);
37703         row.addClass("x-grid-row-selected");
37704     },
37705
37706     onRowDeselect : function(rowIndex){
37707         var row = this.getRowComposite(rowIndex);
37708         row.removeClass("x-grid-row-selected");
37709     },
37710
37711     onCellSelect : function(row, col){
37712         var cell = this.getCell(row, col);
37713         if(cell){
37714             Roo.fly(cell).addClass("x-grid-cell-selected");
37715         }
37716     },
37717
37718     onCellDeselect : function(row, col){
37719         var cell = this.getCell(row, col);
37720         if(cell){
37721             Roo.fly(cell).removeClass("x-grid-cell-selected");
37722         }
37723     },
37724
37725     updateHeaderSortState : function(){
37726         
37727         // sort state can be single { field: xxx, direction : yyy}
37728         // or   { xxx=>ASC , yyy : DESC ..... }
37729         
37730         var mstate = {};
37731         if (!this.ds.multiSort) { 
37732             var state = this.ds.getSortState();
37733             if(!state){
37734                 return;
37735             }
37736             mstate[state.field] = state.direction;
37737             // FIXME... - this is not used here.. but might be elsewhere..
37738             this.sortState = state;
37739             
37740         } else {
37741             mstate = this.ds.sortToggle;
37742         }
37743         //remove existing sort classes..
37744         
37745         var sc = this.sortClasses;
37746         var hds = this.el.select(this.headerSelector).removeClass(sc);
37747         
37748         for(var f in mstate) {
37749         
37750             var sortColumn = this.cm.findColumnIndex(f);
37751             
37752             if(sortColumn != -1){
37753                 var sortDir = mstate[f];        
37754                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
37755             }
37756         }
37757         
37758          
37759         
37760     },
37761
37762
37763     handleHeaderClick : function(g, index,e){
37764         
37765         Roo.log("header click");
37766         
37767         if (Roo.isTouch) {
37768             // touch events on header are handled by context
37769             this.handleHdCtx(g,index,e);
37770             return;
37771         }
37772         
37773         
37774         if(this.headersDisabled){
37775             return;
37776         }
37777         var dm = g.dataSource, cm = g.colModel;
37778         if(!cm.isSortable(index)){
37779             return;
37780         }
37781         g.stopEditing();
37782         
37783         if (dm.multiSort) {
37784             // update the sortOrder
37785             var so = [];
37786             for(var i = 0; i < cm.config.length; i++ ) {
37787                 
37788                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
37789                     continue; // dont' bother, it's not in sort list or being set.
37790                 }
37791                 
37792                 so.push(cm.config[i].dataIndex);
37793             };
37794             dm.sortOrder = so;
37795         }
37796         
37797         
37798         dm.sort(cm.getDataIndex(index));
37799     },
37800
37801
37802     destroy : function(){
37803         if(this.colMenu){
37804             this.colMenu.removeAll();
37805             Roo.menu.MenuMgr.unregister(this.colMenu);
37806             this.colMenu.getEl().remove();
37807             delete this.colMenu;
37808         }
37809         if(this.hmenu){
37810             this.hmenu.removeAll();
37811             Roo.menu.MenuMgr.unregister(this.hmenu);
37812             this.hmenu.getEl().remove();
37813             delete this.hmenu;
37814         }
37815         if(this.grid.enableColumnMove){
37816             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
37817             if(dds){
37818                 for(var dd in dds){
37819                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
37820                         var elid = dds[dd].dragElId;
37821                         dds[dd].unreg();
37822                         Roo.get(elid).remove();
37823                     } else if(dds[dd].config.isTarget){
37824                         dds[dd].proxyTop.remove();
37825                         dds[dd].proxyBottom.remove();
37826                         dds[dd].unreg();
37827                     }
37828                     if(Roo.dd.DDM.locationCache[dd]){
37829                         delete Roo.dd.DDM.locationCache[dd];
37830                     }
37831                 }
37832                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
37833             }
37834         }
37835         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
37836         this.bind(null, null);
37837         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
37838     },
37839
37840     handleLockChange : function(){
37841         this.refresh(true);
37842     },
37843
37844     onDenyColumnLock : function(){
37845
37846     },
37847
37848     onDenyColumnHide : function(){
37849
37850     },
37851
37852     handleHdMenuClick : function(item){
37853         var index = this.hdCtxIndex;
37854         var cm = this.cm, ds = this.ds;
37855         switch(item.id){
37856             case "asc":
37857                 ds.sort(cm.getDataIndex(index), "ASC");
37858                 break;
37859             case "desc":
37860                 ds.sort(cm.getDataIndex(index), "DESC");
37861                 break;
37862             case "lock":
37863                 var lc = cm.getLockedCount();
37864                 if(cm.getColumnCount(true) <= lc+1){
37865                     this.onDenyColumnLock();
37866                     return;
37867                 }
37868                 if(lc != index){
37869                     cm.setLocked(index, true, true);
37870                     cm.moveColumn(index, lc);
37871                     this.grid.fireEvent("columnmove", index, lc);
37872                 }else{
37873                     cm.setLocked(index, true);
37874                 }
37875             break;
37876             case "unlock":
37877                 var lc = cm.getLockedCount();
37878                 if((lc-1) != index){
37879                     cm.setLocked(index, false, true);
37880                     cm.moveColumn(index, lc-1);
37881                     this.grid.fireEvent("columnmove", index, lc-1);
37882                 }else{
37883                     cm.setLocked(index, false);
37884                 }
37885             break;
37886             case 'wider': // used to expand cols on touch..
37887             case 'narrow':
37888                 var cw = cm.getColumnWidth(index);
37889                 cw += (item.id == 'wider' ? 1 : -1) * 50;
37890                 cw = Math.max(0, cw);
37891                 cw = Math.min(cw,4000);
37892                 cm.setColumnWidth(index, cw);
37893                 break;
37894                 
37895             default:
37896                 index = cm.getIndexById(item.id.substr(4));
37897                 if(index != -1){
37898                     if(item.checked && cm.getColumnCount(true) <= 1){
37899                         this.onDenyColumnHide();
37900                         return false;
37901                     }
37902                     cm.setHidden(index, item.checked);
37903                 }
37904         }
37905         return true;
37906     },
37907
37908     beforeColMenuShow : function(){
37909         var cm = this.cm,  colCount = cm.getColumnCount();
37910         this.colMenu.removeAll();
37911         for(var i = 0; i < colCount; i++){
37912             this.colMenu.add(new Roo.menu.CheckItem({
37913                 id: "col-"+cm.getColumnId(i),
37914                 text: cm.getColumnHeader(i),
37915                 checked: !cm.isHidden(i),
37916                 hideOnClick:false
37917             }));
37918         }
37919     },
37920
37921     handleHdCtx : function(g, index, e){
37922         e.stopEvent();
37923         var hd = this.getHeaderCell(index);
37924         this.hdCtxIndex = index;
37925         var ms = this.hmenu.items, cm = this.cm;
37926         ms.get("asc").setDisabled(!cm.isSortable(index));
37927         ms.get("desc").setDisabled(!cm.isSortable(index));
37928         if(this.grid.enableColLock !== false){
37929             ms.get("lock").setDisabled(cm.isLocked(index));
37930             ms.get("unlock").setDisabled(!cm.isLocked(index));
37931         }
37932         this.hmenu.show(hd, "tl-bl");
37933     },
37934
37935     handleHdOver : function(e){
37936         var hd = this.findHeaderCell(e.getTarget());
37937         if(hd && !this.headersDisabled){
37938             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
37939                this.fly(hd).addClass("x-grid-hd-over");
37940             }
37941         }
37942     },
37943
37944     handleHdOut : function(e){
37945         var hd = this.findHeaderCell(e.getTarget());
37946         if(hd){
37947             this.fly(hd).removeClass("x-grid-hd-over");
37948         }
37949     },
37950
37951     handleSplitDblClick : function(e, t){
37952         var i = this.getCellIndex(t);
37953         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
37954             this.autoSizeColumn(i, true);
37955             this.layout();
37956         }
37957     },
37958
37959     render : function(){
37960
37961         var cm = this.cm;
37962         var colCount = cm.getColumnCount();
37963
37964         if(this.grid.monitorWindowResize === true){
37965             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37966         }
37967         var header = this.renderHeaders();
37968         var body = this.templates.body.apply({rows:""});
37969         var html = this.templates.master.apply({
37970             lockedBody: body,
37971             body: body,
37972             lockedHeader: header[0],
37973             header: header[1]
37974         });
37975
37976         //this.updateColumns();
37977
37978         this.grid.getGridEl().dom.innerHTML = html;
37979
37980         this.initElements();
37981         
37982         // a kludge to fix the random scolling effect in webkit
37983         this.el.on("scroll", function() {
37984             this.el.dom.scrollTop=0; // hopefully not recursive..
37985         },this);
37986
37987         this.scroller.on("scroll", this.handleScroll, this);
37988         this.lockedBody.on("mousewheel", this.handleWheel, this);
37989         this.mainBody.on("mousewheel", this.handleWheel, this);
37990
37991         this.mainHd.on("mouseover", this.handleHdOver, this);
37992         this.mainHd.on("mouseout", this.handleHdOut, this);
37993         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
37994                 {delegate: "."+this.splitClass});
37995
37996         this.lockedHd.on("mouseover", this.handleHdOver, this);
37997         this.lockedHd.on("mouseout", this.handleHdOut, this);
37998         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
37999                 {delegate: "."+this.splitClass});
38000
38001         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
38002             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38003         }
38004
38005         this.updateSplitters();
38006
38007         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
38008             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38009             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38010         }
38011
38012         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
38013             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
38014             this.hmenu.add(
38015                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
38016                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
38017             );
38018             if(this.grid.enableColLock !== false){
38019                 this.hmenu.add('-',
38020                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
38021                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
38022                 );
38023             }
38024             if (Roo.isTouch) {
38025                  this.hmenu.add('-',
38026                     {id:"wider", text: this.columnsWiderText},
38027                     {id:"narrow", text: this.columnsNarrowText }
38028                 );
38029                 
38030                  
38031             }
38032             
38033             if(this.grid.enableColumnHide !== false){
38034
38035                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
38036                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
38037                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
38038
38039                 this.hmenu.add('-',
38040                     {id:"columns", text: this.columnsText, menu: this.colMenu}
38041                 );
38042             }
38043             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
38044
38045             this.grid.on("headercontextmenu", this.handleHdCtx, this);
38046         }
38047
38048         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
38049             this.dd = new Roo.grid.GridDragZone(this.grid, {
38050                 ddGroup : this.grid.ddGroup || 'GridDD'
38051             });
38052             
38053         }
38054
38055         /*
38056         for(var i = 0; i < colCount; i++){
38057             if(cm.isHidden(i)){
38058                 this.hideColumn(i);
38059             }
38060             if(cm.config[i].align){
38061                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
38062                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
38063             }
38064         }*/
38065         
38066         this.updateHeaderSortState();
38067
38068         this.beforeInitialResize();
38069         this.layout(true);
38070
38071         // two part rendering gives faster view to the user
38072         this.renderPhase2.defer(1, this);
38073     },
38074
38075     renderPhase2 : function(){
38076         // render the rows now
38077         this.refresh();
38078         if(this.grid.autoSizeColumns){
38079             this.autoSizeColumns();
38080         }
38081     },
38082
38083     beforeInitialResize : function(){
38084
38085     },
38086
38087     onColumnSplitterMoved : function(i, w){
38088         this.userResized = true;
38089         var cm = this.grid.colModel;
38090         cm.setColumnWidth(i, w, true);
38091         var cid = cm.getColumnId(i);
38092         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
38093         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
38094         this.updateSplitters();
38095         this.layout();
38096         this.grid.fireEvent("columnresize", i, w);
38097     },
38098
38099     syncRowHeights : function(startIndex, endIndex){
38100         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
38101             startIndex = startIndex || 0;
38102             var mrows = this.getBodyTable().rows;
38103             var lrows = this.getLockedTable().rows;
38104             var len = mrows.length-1;
38105             endIndex = Math.min(endIndex || len, len);
38106             for(var i = startIndex; i <= endIndex; i++){
38107                 var m = mrows[i], l = lrows[i];
38108                 var h = Math.max(m.offsetHeight, l.offsetHeight);
38109                 m.style.height = l.style.height = h + "px";
38110             }
38111         }
38112     },
38113
38114     layout : function(initialRender, is2ndPass){
38115         var g = this.grid;
38116         var auto = g.autoHeight;
38117         var scrollOffset = 16;
38118         var c = g.getGridEl(), cm = this.cm,
38119                 expandCol = g.autoExpandColumn,
38120                 gv = this;
38121         //c.beginMeasure();
38122
38123         if(!c.dom.offsetWidth){ // display:none?
38124             if(initialRender){
38125                 this.lockedWrap.show();
38126                 this.mainWrap.show();
38127             }
38128             return;
38129         }
38130
38131         var hasLock = this.cm.isLocked(0);
38132
38133         var tbh = this.headerPanel.getHeight();
38134         var bbh = this.footerPanel.getHeight();
38135
38136         if(auto){
38137             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
38138             var newHeight = ch + c.getBorderWidth("tb");
38139             if(g.maxHeight){
38140                 newHeight = Math.min(g.maxHeight, newHeight);
38141             }
38142             c.setHeight(newHeight);
38143         }
38144
38145         if(g.autoWidth){
38146             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
38147         }
38148
38149         var s = this.scroller;
38150
38151         var csize = c.getSize(true);
38152
38153         this.el.setSize(csize.width, csize.height);
38154
38155         this.headerPanel.setWidth(csize.width);
38156         this.footerPanel.setWidth(csize.width);
38157
38158         var hdHeight = this.mainHd.getHeight();
38159         var vw = csize.width;
38160         var vh = csize.height - (tbh + bbh);
38161
38162         s.setSize(vw, vh);
38163
38164         var bt = this.getBodyTable();
38165         var ltWidth = hasLock ?
38166                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
38167
38168         var scrollHeight = bt.offsetHeight;
38169         var scrollWidth = ltWidth + bt.offsetWidth;
38170         var vscroll = false, hscroll = false;
38171
38172         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
38173
38174         var lw = this.lockedWrap, mw = this.mainWrap;
38175         var lb = this.lockedBody, mb = this.mainBody;
38176
38177         setTimeout(function(){
38178             var t = s.dom.offsetTop;
38179             var w = s.dom.clientWidth,
38180                 h = s.dom.clientHeight;
38181
38182             lw.setTop(t);
38183             lw.setSize(ltWidth, h);
38184
38185             mw.setLeftTop(ltWidth, t);
38186             mw.setSize(w-ltWidth, h);
38187
38188             lb.setHeight(h-hdHeight);
38189             mb.setHeight(h-hdHeight);
38190
38191             if(is2ndPass !== true && !gv.userResized && expandCol){
38192                 // high speed resize without full column calculation
38193                 
38194                 var ci = cm.getIndexById(expandCol);
38195                 if (ci < 0) {
38196                     ci = cm.findColumnIndex(expandCol);
38197                 }
38198                 ci = Math.max(0, ci); // make sure it's got at least the first col.
38199                 var expandId = cm.getColumnId(ci);
38200                 var  tw = cm.getTotalWidth(false);
38201                 var currentWidth = cm.getColumnWidth(ci);
38202                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
38203                 if(currentWidth != cw){
38204                     cm.setColumnWidth(ci, cw, true);
38205                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38206                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38207                     gv.updateSplitters();
38208                     gv.layout(false, true);
38209                 }
38210             }
38211
38212             if(initialRender){
38213                 lw.show();
38214                 mw.show();
38215             }
38216             //c.endMeasure();
38217         }, 10);
38218     },
38219
38220     onWindowResize : function(){
38221         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
38222             return;
38223         }
38224         this.layout();
38225     },
38226
38227     appendFooter : function(parentEl){
38228         return null;
38229     },
38230
38231     sortAscText : "Sort Ascending",
38232     sortDescText : "Sort Descending",
38233     lockText : "Lock Column",
38234     unlockText : "Unlock Column",
38235     columnsText : "Columns",
38236  
38237     columnsWiderText : "Wider",
38238     columnsNarrowText : "Thinner"
38239 });
38240
38241
38242 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
38243     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
38244     this.proxy.el.addClass('x-grid3-col-dd');
38245 };
38246
38247 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
38248     handleMouseDown : function(e){
38249
38250     },
38251
38252     callHandleMouseDown : function(e){
38253         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
38254     }
38255 });
38256 /*
38257  * Based on:
38258  * Ext JS Library 1.1.1
38259  * Copyright(c) 2006-2007, Ext JS, LLC.
38260  *
38261  * Originally Released Under LGPL - original licence link has changed is not relivant.
38262  *
38263  * Fork - LGPL
38264  * <script type="text/javascript">
38265  */
38266  
38267 // private
38268 // This is a support class used internally by the Grid components
38269 Roo.grid.SplitDragZone = function(grid, hd, hd2){
38270     this.grid = grid;
38271     this.view = grid.getView();
38272     this.proxy = this.view.resizeProxy;
38273     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
38274         "gridSplitters" + this.grid.getGridEl().id, {
38275         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
38276     });
38277     this.setHandleElId(Roo.id(hd));
38278     this.setOuterHandleElId(Roo.id(hd2));
38279     this.scroll = false;
38280 };
38281 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
38282     fly: Roo.Element.fly,
38283
38284     b4StartDrag : function(x, y){
38285         this.view.headersDisabled = true;
38286         this.proxy.setHeight(this.view.mainWrap.getHeight());
38287         var w = this.cm.getColumnWidth(this.cellIndex);
38288         var minw = Math.max(w-this.grid.minColumnWidth, 0);
38289         this.resetConstraints();
38290         this.setXConstraint(minw, 1000);
38291         this.setYConstraint(0, 0);
38292         this.minX = x - minw;
38293         this.maxX = x + 1000;
38294         this.startPos = x;
38295         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
38296     },
38297
38298
38299     handleMouseDown : function(e){
38300         ev = Roo.EventObject.setEvent(e);
38301         var t = this.fly(ev.getTarget());
38302         if(t.hasClass("x-grid-split")){
38303             this.cellIndex = this.view.getCellIndex(t.dom);
38304             this.split = t.dom;
38305             this.cm = this.grid.colModel;
38306             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
38307                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
38308             }
38309         }
38310     },
38311
38312     endDrag : function(e){
38313         this.view.headersDisabled = false;
38314         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
38315         var diff = endX - this.startPos;
38316         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
38317     },
38318
38319     autoOffset : function(){
38320         this.setDelta(0,0);
38321     }
38322 });/*
38323  * Based on:
38324  * Ext JS Library 1.1.1
38325  * Copyright(c) 2006-2007, Ext JS, LLC.
38326  *
38327  * Originally Released Under LGPL - original licence link has changed is not relivant.
38328  *
38329  * Fork - LGPL
38330  * <script type="text/javascript">
38331  */
38332  
38333 // private
38334 // This is a support class used internally by the Grid components
38335 Roo.grid.GridDragZone = function(grid, config){
38336     this.view = grid.getView();
38337     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
38338     if(this.view.lockedBody){
38339         this.setHandleElId(Roo.id(this.view.mainBody.dom));
38340         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
38341     }
38342     this.scroll = false;
38343     this.grid = grid;
38344     this.ddel = document.createElement('div');
38345     this.ddel.className = 'x-grid-dd-wrap';
38346 };
38347
38348 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
38349     ddGroup : "GridDD",
38350
38351     getDragData : function(e){
38352         var t = Roo.lib.Event.getTarget(e);
38353         var rowIndex = this.view.findRowIndex(t);
38354         var sm = this.grid.selModel;
38355             
38356         //Roo.log(rowIndex);
38357         
38358         if (sm.getSelectedCell) {
38359             // cell selection..
38360             if (!sm.getSelectedCell()) {
38361                 return false;
38362             }
38363             if (rowIndex != sm.getSelectedCell()[0]) {
38364                 return false;
38365             }
38366         
38367         }
38368         
38369         if(rowIndex !== false){
38370             
38371             // if editorgrid.. 
38372             
38373             
38374             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
38375                
38376             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
38377               //  
38378             //}
38379             if (e.hasModifier()){
38380                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
38381             }
38382             
38383             Roo.log("getDragData");
38384             
38385             return {
38386                 grid: this.grid,
38387                 ddel: this.ddel,
38388                 rowIndex: rowIndex,
38389                 selections:sm.getSelections ? sm.getSelections() : (
38390                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
38391                 )
38392             };
38393         }
38394         return false;
38395     },
38396
38397     onInitDrag : function(e){
38398         var data = this.dragData;
38399         this.ddel.innerHTML = this.grid.getDragDropText();
38400         this.proxy.update(this.ddel);
38401         // fire start drag?
38402     },
38403
38404     afterRepair : function(){
38405         this.dragging = false;
38406     },
38407
38408     getRepairXY : function(e, data){
38409         return false;
38410     },
38411
38412     onEndDrag : function(data, e){
38413         // fire end drag?
38414     },
38415
38416     onValidDrop : function(dd, e, id){
38417         // fire drag drop?
38418         this.hideProxy();
38419     },
38420
38421     beforeInvalidDrop : function(e, id){
38422
38423     }
38424 });/*
38425  * Based on:
38426  * Ext JS Library 1.1.1
38427  * Copyright(c) 2006-2007, Ext JS, LLC.
38428  *
38429  * Originally Released Under LGPL - original licence link has changed is not relivant.
38430  *
38431  * Fork - LGPL
38432  * <script type="text/javascript">
38433  */
38434  
38435
38436 /**
38437  * @class Roo.grid.ColumnModel
38438  * @extends Roo.util.Observable
38439  * This is the default implementation of a ColumnModel used by the Grid. It defines
38440  * the columns in the grid.
38441  * <br>Usage:<br>
38442  <pre><code>
38443  var colModel = new Roo.grid.ColumnModel([
38444         {header: "Ticker", width: 60, sortable: true, locked: true},
38445         {header: "Company Name", width: 150, sortable: true},
38446         {header: "Market Cap.", width: 100, sortable: true},
38447         {header: "$ Sales", width: 100, sortable: true, renderer: money},
38448         {header: "Employees", width: 100, sortable: true, resizable: false}
38449  ]);
38450  </code></pre>
38451  * <p>
38452  
38453  * The config options listed for this class are options which may appear in each
38454  * individual column definition.
38455  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
38456  * @constructor
38457  * @param {Object} config An Array of column config objects. See this class's
38458  * config objects for details.
38459 */
38460 Roo.grid.ColumnModel = function(config){
38461         /**
38462      * The config passed into the constructor
38463      */
38464     this.config = config;
38465     this.lookup = {};
38466
38467     // if no id, create one
38468     // if the column does not have a dataIndex mapping,
38469     // map it to the order it is in the config
38470     for(var i = 0, len = config.length; i < len; i++){
38471         var c = config[i];
38472         if(typeof c.dataIndex == "undefined"){
38473             c.dataIndex = i;
38474         }
38475         if(typeof c.renderer == "string"){
38476             c.renderer = Roo.util.Format[c.renderer];
38477         }
38478         if(typeof c.id == "undefined"){
38479             c.id = Roo.id();
38480         }
38481         if(c.editor && c.editor.xtype){
38482             c.editor  = Roo.factory(c.editor, Roo.grid);
38483         }
38484         if(c.editor && c.editor.isFormField){
38485             c.editor = new Roo.grid.GridEditor(c.editor);
38486         }
38487         this.lookup[c.id] = c;
38488     }
38489
38490     /**
38491      * The width of columns which have no width specified (defaults to 100)
38492      * @type Number
38493      */
38494     this.defaultWidth = 100;
38495
38496     /**
38497      * Default sortable of columns which have no sortable specified (defaults to false)
38498      * @type Boolean
38499      */
38500     this.defaultSortable = false;
38501
38502     this.addEvents({
38503         /**
38504              * @event widthchange
38505              * Fires when the width of a column changes.
38506              * @param {ColumnModel} this
38507              * @param {Number} columnIndex The column index
38508              * @param {Number} newWidth The new width
38509              */
38510             "widthchange": true,
38511         /**
38512              * @event headerchange
38513              * Fires when the text of a header changes.
38514              * @param {ColumnModel} this
38515              * @param {Number} columnIndex The column index
38516              * @param {Number} newText The new header text
38517              */
38518             "headerchange": true,
38519         /**
38520              * @event hiddenchange
38521              * Fires when a column is hidden or "unhidden".
38522              * @param {ColumnModel} this
38523              * @param {Number} columnIndex The column index
38524              * @param {Boolean} hidden true if hidden, false otherwise
38525              */
38526             "hiddenchange": true,
38527             /**
38528          * @event columnmoved
38529          * Fires when a column is moved.
38530          * @param {ColumnModel} this
38531          * @param {Number} oldIndex
38532          * @param {Number} newIndex
38533          */
38534         "columnmoved" : true,
38535         /**
38536          * @event columlockchange
38537          * Fires when a column's locked state is changed
38538          * @param {ColumnModel} this
38539          * @param {Number} colIndex
38540          * @param {Boolean} locked true if locked
38541          */
38542         "columnlockchange" : true
38543     });
38544     Roo.grid.ColumnModel.superclass.constructor.call(this);
38545 };
38546 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
38547     /**
38548      * @cfg {String} header The header text to display in the Grid view.
38549      */
38550     /**
38551      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
38552      * {@link Roo.data.Record} definition from which to draw the column's value. If not
38553      * specified, the column's index is used as an index into the Record's data Array.
38554      */
38555     /**
38556      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
38557      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
38558      */
38559     /**
38560      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
38561      * Defaults to the value of the {@link #defaultSortable} property.
38562      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
38563      */
38564     /**
38565      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
38566      */
38567     /**
38568      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
38569      */
38570     /**
38571      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
38572      */
38573     /**
38574      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
38575      */
38576     /**
38577      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
38578      * given the cell's data value. See {@link #setRenderer}. If not specified, the
38579      * default renderer uses the raw data value. If an object is returned (bootstrap only)
38580      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
38581      */
38582        /**
38583      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
38584      */
38585     /**
38586      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
38587      */
38588     /**
38589      * @cfg {String} cursor (Optional)
38590      */
38591     /**
38592      * Returns the id of the column at the specified index.
38593      * @param {Number} index The column index
38594      * @return {String} the id
38595      */
38596     getColumnId : function(index){
38597         return this.config[index].id;
38598     },
38599
38600     /**
38601      * Returns the column for a specified id.
38602      * @param {String} id The column id
38603      * @return {Object} the column
38604      */
38605     getColumnById : function(id){
38606         return this.lookup[id];
38607     },
38608
38609     
38610     /**
38611      * Returns the column for a specified dataIndex.
38612      * @param {String} dataIndex The column dataIndex
38613      * @return {Object|Boolean} the column or false if not found
38614      */
38615     getColumnByDataIndex: function(dataIndex){
38616         var index = this.findColumnIndex(dataIndex);
38617         return index > -1 ? this.config[index] : false;
38618     },
38619     
38620     /**
38621      * Returns the index for a specified column id.
38622      * @param {String} id The column id
38623      * @return {Number} the index, or -1 if not found
38624      */
38625     getIndexById : function(id){
38626         for(var i = 0, len = this.config.length; i < len; i++){
38627             if(this.config[i].id == id){
38628                 return i;
38629             }
38630         }
38631         return -1;
38632     },
38633     
38634     /**
38635      * Returns the index for a specified column dataIndex.
38636      * @param {String} dataIndex The column dataIndex
38637      * @return {Number} the index, or -1 if not found
38638      */
38639     
38640     findColumnIndex : function(dataIndex){
38641         for(var i = 0, len = this.config.length; i < len; i++){
38642             if(this.config[i].dataIndex == dataIndex){
38643                 return i;
38644             }
38645         }
38646         return -1;
38647     },
38648     
38649     
38650     moveColumn : function(oldIndex, newIndex){
38651         var c = this.config[oldIndex];
38652         this.config.splice(oldIndex, 1);
38653         this.config.splice(newIndex, 0, c);
38654         this.dataMap = null;
38655         this.fireEvent("columnmoved", this, oldIndex, newIndex);
38656     },
38657
38658     isLocked : function(colIndex){
38659         return this.config[colIndex].locked === true;
38660     },
38661
38662     setLocked : function(colIndex, value, suppressEvent){
38663         if(this.isLocked(colIndex) == value){
38664             return;
38665         }
38666         this.config[colIndex].locked = value;
38667         if(!suppressEvent){
38668             this.fireEvent("columnlockchange", this, colIndex, value);
38669         }
38670     },
38671
38672     getTotalLockedWidth : function(){
38673         var totalWidth = 0;
38674         for(var i = 0; i < this.config.length; i++){
38675             if(this.isLocked(i) && !this.isHidden(i)){
38676                 this.totalWidth += this.getColumnWidth(i);
38677             }
38678         }
38679         return totalWidth;
38680     },
38681
38682     getLockedCount : function(){
38683         for(var i = 0, len = this.config.length; i < len; i++){
38684             if(!this.isLocked(i)){
38685                 return i;
38686             }
38687         }
38688     },
38689
38690     /**
38691      * Returns the number of columns.
38692      * @return {Number}
38693      */
38694     getColumnCount : function(visibleOnly){
38695         if(visibleOnly === true){
38696             var c = 0;
38697             for(var i = 0, len = this.config.length; i < len; i++){
38698                 if(!this.isHidden(i)){
38699                     c++;
38700                 }
38701             }
38702             return c;
38703         }
38704         return this.config.length;
38705     },
38706
38707     /**
38708      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
38709      * @param {Function} fn
38710      * @param {Object} scope (optional)
38711      * @return {Array} result
38712      */
38713     getColumnsBy : function(fn, scope){
38714         var r = [];
38715         for(var i = 0, len = this.config.length; i < len; i++){
38716             var c = this.config[i];
38717             if(fn.call(scope||this, c, i) === true){
38718                 r[r.length] = c;
38719             }
38720         }
38721         return r;
38722     },
38723
38724     /**
38725      * Returns true if the specified column is sortable.
38726      * @param {Number} col The column index
38727      * @return {Boolean}
38728      */
38729     isSortable : function(col){
38730         if(typeof this.config[col].sortable == "undefined"){
38731             return this.defaultSortable;
38732         }
38733         return this.config[col].sortable;
38734     },
38735
38736     /**
38737      * Returns the rendering (formatting) function defined for the column.
38738      * @param {Number} col The column index.
38739      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
38740      */
38741     getRenderer : function(col){
38742         if(!this.config[col].renderer){
38743             return Roo.grid.ColumnModel.defaultRenderer;
38744         }
38745         return this.config[col].renderer;
38746     },
38747
38748     /**
38749      * Sets the rendering (formatting) function for a column.
38750      * @param {Number} col The column index
38751      * @param {Function} fn The function to use to process the cell's raw data
38752      * to return HTML markup for the grid view. The render function is called with
38753      * the following parameters:<ul>
38754      * <li>Data value.</li>
38755      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
38756      * <li>css A CSS style string to apply to the table cell.</li>
38757      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
38758      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
38759      * <li>Row index</li>
38760      * <li>Column index</li>
38761      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
38762      */
38763     setRenderer : function(col, fn){
38764         this.config[col].renderer = fn;
38765     },
38766
38767     /**
38768      * Returns the width for the specified column.
38769      * @param {Number} col The column index
38770      * @return {Number}
38771      */
38772     getColumnWidth : function(col){
38773         return this.config[col].width * 1 || this.defaultWidth;
38774     },
38775
38776     /**
38777      * Sets the width for a column.
38778      * @param {Number} col The column index
38779      * @param {Number} width The new width
38780      */
38781     setColumnWidth : function(col, width, suppressEvent){
38782         this.config[col].width = width;
38783         this.totalWidth = null;
38784         if(!suppressEvent){
38785              this.fireEvent("widthchange", this, col, width);
38786         }
38787     },
38788
38789     /**
38790      * Returns the total width of all columns.
38791      * @param {Boolean} includeHidden True to include hidden column widths
38792      * @return {Number}
38793      */
38794     getTotalWidth : function(includeHidden){
38795         if(!this.totalWidth){
38796             this.totalWidth = 0;
38797             for(var i = 0, len = this.config.length; i < len; i++){
38798                 if(includeHidden || !this.isHidden(i)){
38799                     this.totalWidth += this.getColumnWidth(i);
38800                 }
38801             }
38802         }
38803         return this.totalWidth;
38804     },
38805
38806     /**
38807      * Returns the header for the specified column.
38808      * @param {Number} col The column index
38809      * @return {String}
38810      */
38811     getColumnHeader : function(col){
38812         return this.config[col].header;
38813     },
38814
38815     /**
38816      * Sets the header for a column.
38817      * @param {Number} col The column index
38818      * @param {String} header The new header
38819      */
38820     setColumnHeader : function(col, header){
38821         this.config[col].header = header;
38822         this.fireEvent("headerchange", this, col, header);
38823     },
38824
38825     /**
38826      * Returns the tooltip for the specified column.
38827      * @param {Number} col The column index
38828      * @return {String}
38829      */
38830     getColumnTooltip : function(col){
38831             return this.config[col].tooltip;
38832     },
38833     /**
38834      * Sets the tooltip for a column.
38835      * @param {Number} col The column index
38836      * @param {String} tooltip The new tooltip
38837      */
38838     setColumnTooltip : function(col, tooltip){
38839             this.config[col].tooltip = tooltip;
38840     },
38841
38842     /**
38843      * Returns the dataIndex for the specified column.
38844      * @param {Number} col The column index
38845      * @return {Number}
38846      */
38847     getDataIndex : function(col){
38848         return this.config[col].dataIndex;
38849     },
38850
38851     /**
38852      * Sets the dataIndex for a column.
38853      * @param {Number} col The column index
38854      * @param {Number} dataIndex The new dataIndex
38855      */
38856     setDataIndex : function(col, dataIndex){
38857         this.config[col].dataIndex = dataIndex;
38858     },
38859
38860     
38861     
38862     /**
38863      * Returns true if the cell is editable.
38864      * @param {Number} colIndex The column index
38865      * @param {Number} rowIndex The row index
38866      * @return {Boolean}
38867      */
38868     isCellEditable : function(colIndex, rowIndex){
38869         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
38870     },
38871
38872     /**
38873      * Returns the editor defined for the cell/column.
38874      * return false or null to disable editing.
38875      * @param {Number} colIndex The column index
38876      * @param {Number} rowIndex The row index
38877      * @return {Object}
38878      */
38879     getCellEditor : function(colIndex, rowIndex){
38880         return this.config[colIndex].editor;
38881     },
38882
38883     /**
38884      * Sets if a column is editable.
38885      * @param {Number} col The column index
38886      * @param {Boolean} editable True if the column is editable
38887      */
38888     setEditable : function(col, editable){
38889         this.config[col].editable = editable;
38890     },
38891
38892
38893     /**
38894      * Returns true if the column is hidden.
38895      * @param {Number} colIndex The column index
38896      * @return {Boolean}
38897      */
38898     isHidden : function(colIndex){
38899         return this.config[colIndex].hidden;
38900     },
38901
38902
38903     /**
38904      * Returns true if the column width cannot be changed
38905      */
38906     isFixed : function(colIndex){
38907         return this.config[colIndex].fixed;
38908     },
38909
38910     /**
38911      * Returns true if the column can be resized
38912      * @return {Boolean}
38913      */
38914     isResizable : function(colIndex){
38915         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
38916     },
38917     /**
38918      * Sets if a column is hidden.
38919      * @param {Number} colIndex The column index
38920      * @param {Boolean} hidden True if the column is hidden
38921      */
38922     setHidden : function(colIndex, hidden){
38923         this.config[colIndex].hidden = hidden;
38924         this.totalWidth = null;
38925         this.fireEvent("hiddenchange", this, colIndex, hidden);
38926     },
38927
38928     /**
38929      * Sets the editor for a column.
38930      * @param {Number} col The column index
38931      * @param {Object} editor The editor object
38932      */
38933     setEditor : function(col, editor){
38934         this.config[col].editor = editor;
38935     }
38936 });
38937
38938 Roo.grid.ColumnModel.defaultRenderer = function(value){
38939         if(typeof value == "string" && value.length < 1){
38940             return "&#160;";
38941         }
38942         return value;
38943 };
38944
38945 // Alias for backwards compatibility
38946 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
38947 /*
38948  * Based on:
38949  * Ext JS Library 1.1.1
38950  * Copyright(c) 2006-2007, Ext JS, LLC.
38951  *
38952  * Originally Released Under LGPL - original licence link has changed is not relivant.
38953  *
38954  * Fork - LGPL
38955  * <script type="text/javascript">
38956  */
38957
38958 /**
38959  * @class Roo.grid.AbstractSelectionModel
38960  * @extends Roo.util.Observable
38961  * Abstract base class for grid SelectionModels.  It provides the interface that should be
38962  * implemented by descendant classes.  This class should not be directly instantiated.
38963  * @constructor
38964  */
38965 Roo.grid.AbstractSelectionModel = function(){
38966     this.locked = false;
38967     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
38968 };
38969
38970 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
38971     /** @ignore Called by the grid automatically. Do not call directly. */
38972     init : function(grid){
38973         this.grid = grid;
38974         this.initEvents();
38975     },
38976
38977     /**
38978      * Locks the selections.
38979      */
38980     lock : function(){
38981         this.locked = true;
38982     },
38983
38984     /**
38985      * Unlocks the selections.
38986      */
38987     unlock : function(){
38988         this.locked = false;
38989     },
38990
38991     /**
38992      * Returns true if the selections are locked.
38993      * @return {Boolean}
38994      */
38995     isLocked : function(){
38996         return this.locked;
38997     }
38998 });/*
38999  * Based on:
39000  * Ext JS Library 1.1.1
39001  * Copyright(c) 2006-2007, Ext JS, LLC.
39002  *
39003  * Originally Released Under LGPL - original licence link has changed is not relivant.
39004  *
39005  * Fork - LGPL
39006  * <script type="text/javascript">
39007  */
39008 /**
39009  * @extends Roo.grid.AbstractSelectionModel
39010  * @class Roo.grid.RowSelectionModel
39011  * The default SelectionModel used by {@link Roo.grid.Grid}.
39012  * It supports multiple selections and keyboard selection/navigation. 
39013  * @constructor
39014  * @param {Object} config
39015  */
39016 Roo.grid.RowSelectionModel = function(config){
39017     Roo.apply(this, config);
39018     this.selections = new Roo.util.MixedCollection(false, function(o){
39019         return o.id;
39020     });
39021
39022     this.last = false;
39023     this.lastActive = false;
39024
39025     this.addEvents({
39026         /**
39027              * @event selectionchange
39028              * Fires when the selection changes
39029              * @param {SelectionModel} this
39030              */
39031             "selectionchange" : true,
39032         /**
39033              * @event afterselectionchange
39034              * Fires after the selection changes (eg. by key press or clicking)
39035              * @param {SelectionModel} this
39036              */
39037             "afterselectionchange" : true,
39038         /**
39039              * @event beforerowselect
39040              * Fires when a row is selected being selected, return false to cancel.
39041              * @param {SelectionModel} this
39042              * @param {Number} rowIndex The selected index
39043              * @param {Boolean} keepExisting False if other selections will be cleared
39044              */
39045             "beforerowselect" : true,
39046         /**
39047              * @event rowselect
39048              * Fires when a row is selected.
39049              * @param {SelectionModel} this
39050              * @param {Number} rowIndex The selected index
39051              * @param {Roo.data.Record} r The record
39052              */
39053             "rowselect" : true,
39054         /**
39055              * @event rowdeselect
39056              * Fires when a row is deselected.
39057              * @param {SelectionModel} this
39058              * @param {Number} rowIndex The selected index
39059              */
39060         "rowdeselect" : true
39061     });
39062     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
39063     this.locked = false;
39064 };
39065
39066 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
39067     /**
39068      * @cfg {Boolean} singleSelect
39069      * True to allow selection of only one row at a time (defaults to false)
39070      */
39071     singleSelect : false,
39072
39073     // private
39074     initEvents : function(){
39075
39076         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
39077             this.grid.on("mousedown", this.handleMouseDown, this);
39078         }else{ // allow click to work like normal
39079             this.grid.on("rowclick", this.handleDragableRowClick, this);
39080         }
39081
39082         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
39083             "up" : function(e){
39084                 if(!e.shiftKey){
39085                     this.selectPrevious(e.shiftKey);
39086                 }else if(this.last !== false && this.lastActive !== false){
39087                     var last = this.last;
39088                     this.selectRange(this.last,  this.lastActive-1);
39089                     this.grid.getView().focusRow(this.lastActive);
39090                     if(last !== false){
39091                         this.last = last;
39092                     }
39093                 }else{
39094                     this.selectFirstRow();
39095                 }
39096                 this.fireEvent("afterselectionchange", this);
39097             },
39098             "down" : function(e){
39099                 if(!e.shiftKey){
39100                     this.selectNext(e.shiftKey);
39101                 }else if(this.last !== false && this.lastActive !== false){
39102                     var last = this.last;
39103                     this.selectRange(this.last,  this.lastActive+1);
39104                     this.grid.getView().focusRow(this.lastActive);
39105                     if(last !== false){
39106                         this.last = last;
39107                     }
39108                 }else{
39109                     this.selectFirstRow();
39110                 }
39111                 this.fireEvent("afterselectionchange", this);
39112             },
39113             scope: this
39114         });
39115
39116         var view = this.grid.view;
39117         view.on("refresh", this.onRefresh, this);
39118         view.on("rowupdated", this.onRowUpdated, this);
39119         view.on("rowremoved", this.onRemove, this);
39120     },
39121
39122     // private
39123     onRefresh : function(){
39124         var ds = this.grid.dataSource, i, v = this.grid.view;
39125         var s = this.selections;
39126         s.each(function(r){
39127             if((i = ds.indexOfId(r.id)) != -1){
39128                 v.onRowSelect(i);
39129             }else{
39130                 s.remove(r);
39131             }
39132         });
39133     },
39134
39135     // private
39136     onRemove : function(v, index, r){
39137         this.selections.remove(r);
39138     },
39139
39140     // private
39141     onRowUpdated : function(v, index, r){
39142         if(this.isSelected(r)){
39143             v.onRowSelect(index);
39144         }
39145     },
39146
39147     /**
39148      * Select records.
39149      * @param {Array} records The records to select
39150      * @param {Boolean} keepExisting (optional) True to keep existing selections
39151      */
39152     selectRecords : function(records, keepExisting){
39153         if(!keepExisting){
39154             this.clearSelections();
39155         }
39156         var ds = this.grid.dataSource;
39157         for(var i = 0, len = records.length; i < len; i++){
39158             this.selectRow(ds.indexOf(records[i]), true);
39159         }
39160     },
39161
39162     /**
39163      * Gets the number of selected rows.
39164      * @return {Number}
39165      */
39166     getCount : function(){
39167         return this.selections.length;
39168     },
39169
39170     /**
39171      * Selects the first row in the grid.
39172      */
39173     selectFirstRow : function(){
39174         this.selectRow(0);
39175     },
39176
39177     /**
39178      * Select the last row.
39179      * @param {Boolean} keepExisting (optional) True to keep existing selections
39180      */
39181     selectLastRow : function(keepExisting){
39182         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
39183     },
39184
39185     /**
39186      * Selects the row immediately following the last selected row.
39187      * @param {Boolean} keepExisting (optional) True to keep existing selections
39188      */
39189     selectNext : function(keepExisting){
39190         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
39191             this.selectRow(this.last+1, keepExisting);
39192             this.grid.getView().focusRow(this.last);
39193         }
39194     },
39195
39196     /**
39197      * Selects the row that precedes the last selected row.
39198      * @param {Boolean} keepExisting (optional) True to keep existing selections
39199      */
39200     selectPrevious : function(keepExisting){
39201         if(this.last){
39202             this.selectRow(this.last-1, keepExisting);
39203             this.grid.getView().focusRow(this.last);
39204         }
39205     },
39206
39207     /**
39208      * Returns the selected records
39209      * @return {Array} Array of selected records
39210      */
39211     getSelections : function(){
39212         return [].concat(this.selections.items);
39213     },
39214
39215     /**
39216      * Returns the first selected record.
39217      * @return {Record}
39218      */
39219     getSelected : function(){
39220         return this.selections.itemAt(0);
39221     },
39222
39223
39224     /**
39225      * Clears all selections.
39226      */
39227     clearSelections : function(fast){
39228         if(this.locked) return;
39229         if(fast !== true){
39230             var ds = this.grid.dataSource;
39231             var s = this.selections;
39232             s.each(function(r){
39233                 this.deselectRow(ds.indexOfId(r.id));
39234             }, this);
39235             s.clear();
39236         }else{
39237             this.selections.clear();
39238         }
39239         this.last = false;
39240     },
39241
39242
39243     /**
39244      * Selects all rows.
39245      */
39246     selectAll : function(){
39247         if(this.locked) return;
39248         this.selections.clear();
39249         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
39250             this.selectRow(i, true);
39251         }
39252     },
39253
39254     /**
39255      * Returns True if there is a selection.
39256      * @return {Boolean}
39257      */
39258     hasSelection : function(){
39259         return this.selections.length > 0;
39260     },
39261
39262     /**
39263      * Returns True if the specified row is selected.
39264      * @param {Number/Record} record The record or index of the record to check
39265      * @return {Boolean}
39266      */
39267     isSelected : function(index){
39268         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
39269         return (r && this.selections.key(r.id) ? true : false);
39270     },
39271
39272     /**
39273      * Returns True if the specified record id is selected.
39274      * @param {String} id The id of record to check
39275      * @return {Boolean}
39276      */
39277     isIdSelected : function(id){
39278         return (this.selections.key(id) ? true : false);
39279     },
39280
39281     // private
39282     handleMouseDown : function(e, t){
39283         var view = this.grid.getView(), rowIndex;
39284         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
39285             return;
39286         };
39287         if(e.shiftKey && this.last !== false){
39288             var last = this.last;
39289             this.selectRange(last, rowIndex, e.ctrlKey);
39290             this.last = last; // reset the last
39291             view.focusRow(rowIndex);
39292         }else{
39293             var isSelected = this.isSelected(rowIndex);
39294             if(e.button !== 0 && isSelected){
39295                 view.focusRow(rowIndex);
39296             }else if(e.ctrlKey && isSelected){
39297                 this.deselectRow(rowIndex);
39298             }else if(!isSelected){
39299                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
39300                 view.focusRow(rowIndex);
39301             }
39302         }
39303         this.fireEvent("afterselectionchange", this);
39304     },
39305     // private
39306     handleDragableRowClick :  function(grid, rowIndex, e) 
39307     {
39308         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
39309             this.selectRow(rowIndex, false);
39310             grid.view.focusRow(rowIndex);
39311              this.fireEvent("afterselectionchange", this);
39312         }
39313     },
39314     
39315     /**
39316      * Selects multiple rows.
39317      * @param {Array} rows Array of the indexes of the row to select
39318      * @param {Boolean} keepExisting (optional) True to keep existing selections
39319      */
39320     selectRows : function(rows, keepExisting){
39321         if(!keepExisting){
39322             this.clearSelections();
39323         }
39324         for(var i = 0, len = rows.length; i < len; i++){
39325             this.selectRow(rows[i], true);
39326         }
39327     },
39328
39329     /**
39330      * Selects a range of rows. All rows in between startRow and endRow are also selected.
39331      * @param {Number} startRow The index of the first row in the range
39332      * @param {Number} endRow The index of the last row in the range
39333      * @param {Boolean} keepExisting (optional) True to retain existing selections
39334      */
39335     selectRange : function(startRow, endRow, keepExisting){
39336         if(this.locked) return;
39337         if(!keepExisting){
39338             this.clearSelections();
39339         }
39340         if(startRow <= endRow){
39341             for(var i = startRow; i <= endRow; i++){
39342                 this.selectRow(i, true);
39343             }
39344         }else{
39345             for(var i = startRow; i >= endRow; i--){
39346                 this.selectRow(i, true);
39347             }
39348         }
39349     },
39350
39351     /**
39352      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
39353      * @param {Number} startRow The index of the first row in the range
39354      * @param {Number} endRow The index of the last row in the range
39355      */
39356     deselectRange : function(startRow, endRow, preventViewNotify){
39357         if(this.locked) return;
39358         for(var i = startRow; i <= endRow; i++){
39359             this.deselectRow(i, preventViewNotify);
39360         }
39361     },
39362
39363     /**
39364      * Selects a row.
39365      * @param {Number} row The index of the row to select
39366      * @param {Boolean} keepExisting (optional) True to keep existing selections
39367      */
39368     selectRow : function(index, keepExisting, preventViewNotify){
39369         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
39370         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
39371             if(!keepExisting || this.singleSelect){
39372                 this.clearSelections();
39373             }
39374             var r = this.grid.dataSource.getAt(index);
39375             this.selections.add(r);
39376             this.last = this.lastActive = index;
39377             if(!preventViewNotify){
39378                 this.grid.getView().onRowSelect(index);
39379             }
39380             this.fireEvent("rowselect", this, index, r);
39381             this.fireEvent("selectionchange", this);
39382         }
39383     },
39384
39385     /**
39386      * Deselects a row.
39387      * @param {Number} row The index of the row to deselect
39388      */
39389     deselectRow : function(index, preventViewNotify){
39390         if(this.locked) return;
39391         if(this.last == index){
39392             this.last = false;
39393         }
39394         if(this.lastActive == index){
39395             this.lastActive = false;
39396         }
39397         var r = this.grid.dataSource.getAt(index);
39398         this.selections.remove(r);
39399         if(!preventViewNotify){
39400             this.grid.getView().onRowDeselect(index);
39401         }
39402         this.fireEvent("rowdeselect", this, index);
39403         this.fireEvent("selectionchange", this);
39404     },
39405
39406     // private
39407     restoreLast : function(){
39408         if(this._last){
39409             this.last = this._last;
39410         }
39411     },
39412
39413     // private
39414     acceptsNav : function(row, col, cm){
39415         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39416     },
39417
39418     // private
39419     onEditorKey : function(field, e){
39420         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
39421         if(k == e.TAB){
39422             e.stopEvent();
39423             ed.completeEdit();
39424             if(e.shiftKey){
39425                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39426             }else{
39427                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39428             }
39429         }else if(k == e.ENTER && !e.ctrlKey){
39430             e.stopEvent();
39431             ed.completeEdit();
39432             if(e.shiftKey){
39433                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
39434             }else{
39435                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
39436             }
39437         }else if(k == e.ESC){
39438             ed.cancelEdit();
39439         }
39440         if(newCell){
39441             g.startEditing(newCell[0], newCell[1]);
39442         }
39443     }
39444 });/*
39445  * Based on:
39446  * Ext JS Library 1.1.1
39447  * Copyright(c) 2006-2007, Ext JS, LLC.
39448  *
39449  * Originally Released Under LGPL - original licence link has changed is not relivant.
39450  *
39451  * Fork - LGPL
39452  * <script type="text/javascript">
39453  */
39454 /**
39455  * @class Roo.grid.CellSelectionModel
39456  * @extends Roo.grid.AbstractSelectionModel
39457  * This class provides the basic implementation for cell selection in a grid.
39458  * @constructor
39459  * @param {Object} config The object containing the configuration of this model.
39460  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
39461  */
39462 Roo.grid.CellSelectionModel = function(config){
39463     Roo.apply(this, config);
39464
39465     this.selection = null;
39466
39467     this.addEvents({
39468         /**
39469              * @event beforerowselect
39470              * Fires before a cell is selected.
39471              * @param {SelectionModel} this
39472              * @param {Number} rowIndex The selected row index
39473              * @param {Number} colIndex The selected cell index
39474              */
39475             "beforecellselect" : true,
39476         /**
39477              * @event cellselect
39478              * Fires when a cell is selected.
39479              * @param {SelectionModel} this
39480              * @param {Number} rowIndex The selected row index
39481              * @param {Number} colIndex The selected cell index
39482              */
39483             "cellselect" : true,
39484         /**
39485              * @event selectionchange
39486              * Fires when the active selection changes.
39487              * @param {SelectionModel} this
39488              * @param {Object} selection null for no selection or an object (o) with two properties
39489                 <ul>
39490                 <li>o.record: the record object for the row the selection is in</li>
39491                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
39492                 </ul>
39493              */
39494             "selectionchange" : true,
39495         /**
39496              * @event tabend
39497              * Fires when the tab (or enter) was pressed on the last editable cell
39498              * You can use this to trigger add new row.
39499              * @param {SelectionModel} this
39500              */
39501             "tabend" : true,
39502          /**
39503              * @event beforeeditnext
39504              * Fires before the next editable sell is made active
39505              * You can use this to skip to another cell or fire the tabend
39506              *    if you set cell to false
39507              * @param {Object} eventdata object : { cell : [ row, col ] } 
39508              */
39509             "beforeeditnext" : true
39510     });
39511     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
39512 };
39513
39514 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
39515     
39516     enter_is_tab: false,
39517
39518     /** @ignore */
39519     initEvents : function(){
39520         this.grid.on("mousedown", this.handleMouseDown, this);
39521         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
39522         var view = this.grid.view;
39523         view.on("refresh", this.onViewChange, this);
39524         view.on("rowupdated", this.onRowUpdated, this);
39525         view.on("beforerowremoved", this.clearSelections, this);
39526         view.on("beforerowsinserted", this.clearSelections, this);
39527         if(this.grid.isEditor){
39528             this.grid.on("beforeedit", this.beforeEdit,  this);
39529         }
39530     },
39531
39532         //private
39533     beforeEdit : function(e){
39534         this.select(e.row, e.column, false, true, e.record);
39535     },
39536
39537         //private
39538     onRowUpdated : function(v, index, r){
39539         if(this.selection && this.selection.record == r){
39540             v.onCellSelect(index, this.selection.cell[1]);
39541         }
39542     },
39543
39544         //private
39545     onViewChange : function(){
39546         this.clearSelections(true);
39547     },
39548
39549         /**
39550          * Returns the currently selected cell,.
39551          * @return {Array} The selected cell (row, column) or null if none selected.
39552          */
39553     getSelectedCell : function(){
39554         return this.selection ? this.selection.cell : null;
39555     },
39556
39557     /**
39558      * Clears all selections.
39559      * @param {Boolean} true to prevent the gridview from being notified about the change.
39560      */
39561     clearSelections : function(preventNotify){
39562         var s = this.selection;
39563         if(s){
39564             if(preventNotify !== true){
39565                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
39566             }
39567             this.selection = null;
39568             this.fireEvent("selectionchange", this, null);
39569         }
39570     },
39571
39572     /**
39573      * Returns true if there is a selection.
39574      * @return {Boolean}
39575      */
39576     hasSelection : function(){
39577         return this.selection ? true : false;
39578     },
39579
39580     /** @ignore */
39581     handleMouseDown : function(e, t){
39582         var v = this.grid.getView();
39583         if(this.isLocked()){
39584             return;
39585         };
39586         var row = v.findRowIndex(t);
39587         var cell = v.findCellIndex(t);
39588         if(row !== false && cell !== false){
39589             this.select(row, cell);
39590         }
39591     },
39592
39593     /**
39594      * Selects a cell.
39595      * @param {Number} rowIndex
39596      * @param {Number} collIndex
39597      */
39598     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
39599         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
39600             this.clearSelections();
39601             r = r || this.grid.dataSource.getAt(rowIndex);
39602             this.selection = {
39603                 record : r,
39604                 cell : [rowIndex, colIndex]
39605             };
39606             if(!preventViewNotify){
39607                 var v = this.grid.getView();
39608                 v.onCellSelect(rowIndex, colIndex);
39609                 if(preventFocus !== true){
39610                     v.focusCell(rowIndex, colIndex);
39611                 }
39612             }
39613             this.fireEvent("cellselect", this, rowIndex, colIndex);
39614             this.fireEvent("selectionchange", this, this.selection);
39615         }
39616     },
39617
39618         //private
39619     isSelectable : function(rowIndex, colIndex, cm){
39620         return !cm.isHidden(colIndex);
39621     },
39622
39623     /** @ignore */
39624     handleKeyDown : function(e){
39625         //Roo.log('Cell Sel Model handleKeyDown');
39626         if(!e.isNavKeyPress()){
39627             return;
39628         }
39629         var g = this.grid, s = this.selection;
39630         if(!s){
39631             e.stopEvent();
39632             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
39633             if(cell){
39634                 this.select(cell[0], cell[1]);
39635             }
39636             return;
39637         }
39638         var sm = this;
39639         var walk = function(row, col, step){
39640             return g.walkCells(row, col, step, sm.isSelectable,  sm);
39641         };
39642         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
39643         var newCell;
39644
39645       
39646
39647         switch(k){
39648             case e.TAB:
39649                 // handled by onEditorKey
39650                 if (g.isEditor && g.editing) {
39651                     return;
39652                 }
39653                 if(e.shiftKey) {
39654                     newCell = walk(r, c-1, -1);
39655                 } else {
39656                     newCell = walk(r, c+1, 1);
39657                 }
39658                 break;
39659             
39660             case e.DOWN:
39661                newCell = walk(r+1, c, 1);
39662                 break;
39663             
39664             case e.UP:
39665                 newCell = walk(r-1, c, -1);
39666                 break;
39667             
39668             case e.RIGHT:
39669                 newCell = walk(r, c+1, 1);
39670                 break;
39671             
39672             case e.LEFT:
39673                 newCell = walk(r, c-1, -1);
39674                 break;
39675             
39676             case e.ENTER:
39677                 
39678                 if(g.isEditor && !g.editing){
39679                    g.startEditing(r, c);
39680                    e.stopEvent();
39681                    return;
39682                 }
39683                 
39684                 
39685              break;
39686         };
39687         if(newCell){
39688             this.select(newCell[0], newCell[1]);
39689             e.stopEvent();
39690             
39691         }
39692     },
39693
39694     acceptsNav : function(row, col, cm){
39695         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39696     },
39697     /**
39698      * Selects a cell.
39699      * @param {Number} field (not used) - as it's normally used as a listener
39700      * @param {Number} e - event - fake it by using
39701      *
39702      * var e = Roo.EventObjectImpl.prototype;
39703      * e.keyCode = e.TAB
39704      *
39705      * 
39706      */
39707     onEditorKey : function(field, e){
39708         
39709         var k = e.getKey(),
39710             newCell,
39711             g = this.grid,
39712             ed = g.activeEditor,
39713             forward = false;
39714         ///Roo.log('onEditorKey' + k);
39715         
39716         
39717         if (this.enter_is_tab && k == e.ENTER) {
39718             k = e.TAB;
39719         }
39720         
39721         if(k == e.TAB){
39722             if(e.shiftKey){
39723                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39724             }else{
39725                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39726                 forward = true;
39727             }
39728             
39729             e.stopEvent();
39730             
39731         } else if(k == e.ENTER &&  !e.ctrlKey){
39732             ed.completeEdit();
39733             e.stopEvent();
39734             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39735         
39736                 } else if(k == e.ESC){
39737             ed.cancelEdit();
39738         }
39739                 
39740         if (newCell) {
39741             var ecall = { cell : newCell, forward : forward };
39742             this.fireEvent('beforeeditnext', ecall );
39743             newCell = ecall.cell;
39744                         forward = ecall.forward;
39745         }
39746                 
39747         if(newCell){
39748             //Roo.log('next cell after edit');
39749             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
39750         } else if (forward) {
39751             // tabbed past last
39752             this.fireEvent.defer(100, this, ['tabend',this]);
39753         }
39754     }
39755 });/*
39756  * Based on:
39757  * Ext JS Library 1.1.1
39758  * Copyright(c) 2006-2007, Ext JS, LLC.
39759  *
39760  * Originally Released Under LGPL - original licence link has changed is not relivant.
39761  *
39762  * Fork - LGPL
39763  * <script type="text/javascript">
39764  */
39765  
39766 /**
39767  * @class Roo.grid.EditorGrid
39768  * @extends Roo.grid.Grid
39769  * Class for creating and editable grid.
39770  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
39771  * The container MUST have some type of size defined for the grid to fill. The container will be 
39772  * automatically set to position relative if it isn't already.
39773  * @param {Object} dataSource The data model to bind to
39774  * @param {Object} colModel The column model with info about this grid's columns
39775  */
39776 Roo.grid.EditorGrid = function(container, config){
39777     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
39778     this.getGridEl().addClass("xedit-grid");
39779
39780     if(!this.selModel){
39781         this.selModel = new Roo.grid.CellSelectionModel();
39782     }
39783
39784     this.activeEditor = null;
39785
39786         this.addEvents({
39787             /**
39788              * @event beforeedit
39789              * Fires before cell editing is triggered. The edit event object has the following properties <br />
39790              * <ul style="padding:5px;padding-left:16px;">
39791              * <li>grid - This grid</li>
39792              * <li>record - The record being edited</li>
39793              * <li>field - The field name being edited</li>
39794              * <li>value - The value for the field being edited.</li>
39795              * <li>row - The grid row index</li>
39796              * <li>column - The grid column index</li>
39797              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
39798              * </ul>
39799              * @param {Object} e An edit event (see above for description)
39800              */
39801             "beforeedit" : true,
39802             /**
39803              * @event afteredit
39804              * Fires after a cell is edited. <br />
39805              * <ul style="padding:5px;padding-left:16px;">
39806              * <li>grid - This grid</li>
39807              * <li>record - The record being edited</li>
39808              * <li>field - The field name being edited</li>
39809              * <li>value - The value being set</li>
39810              * <li>originalValue - The original value for the field, before the edit.</li>
39811              * <li>row - The grid row index</li>
39812              * <li>column - The grid column index</li>
39813              * </ul>
39814              * @param {Object} e An edit event (see above for description)
39815              */
39816             "afteredit" : true,
39817             /**
39818              * @event validateedit
39819              * Fires after a cell is edited, but before the value is set in the record. 
39820          * You can use this to modify the value being set in the field, Return false
39821              * to cancel the change. The edit event object has the following properties <br />
39822              * <ul style="padding:5px;padding-left:16px;">
39823          * <li>editor - This editor</li>
39824              * <li>grid - This grid</li>
39825              * <li>record - The record being edited</li>
39826              * <li>field - The field name being edited</li>
39827              * <li>value - The value being set</li>
39828              * <li>originalValue - The original value for the field, before the edit.</li>
39829              * <li>row - The grid row index</li>
39830              * <li>column - The grid column index</li>
39831              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
39832              * </ul>
39833              * @param {Object} e An edit event (see above for description)
39834              */
39835             "validateedit" : true
39836         });
39837     this.on("bodyscroll", this.stopEditing,  this);
39838     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
39839 };
39840
39841 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
39842     /**
39843      * @cfg {Number} clicksToEdit
39844      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
39845      */
39846     clicksToEdit: 2,
39847
39848     // private
39849     isEditor : true,
39850     // private
39851     trackMouseOver: false, // causes very odd FF errors
39852
39853     onCellDblClick : function(g, row, col){
39854         this.startEditing(row, col);
39855     },
39856
39857     onEditComplete : function(ed, value, startValue){
39858         this.editing = false;
39859         this.activeEditor = null;
39860         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
39861         var r = ed.record;
39862         var field = this.colModel.getDataIndex(ed.col);
39863         var e = {
39864             grid: this,
39865             record: r,
39866             field: field,
39867             originalValue: startValue,
39868             value: value,
39869             row: ed.row,
39870             column: ed.col,
39871             cancel:false,
39872             editor: ed
39873         };
39874         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
39875         cell.show();
39876           
39877         if(String(value) !== String(startValue)){
39878             
39879             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
39880                 r.set(field, e.value);
39881                 // if we are dealing with a combo box..
39882                 // then we also set the 'name' colum to be the displayField
39883                 if (ed.field.displayField && ed.field.name) {
39884                     r.set(ed.field.name, ed.field.el.dom.value);
39885                 }
39886                 
39887                 delete e.cancel; //?? why!!!
39888                 this.fireEvent("afteredit", e);
39889             }
39890         } else {
39891             this.fireEvent("afteredit", e); // always fire it!
39892         }
39893         this.view.focusCell(ed.row, ed.col);
39894     },
39895
39896     /**
39897      * Starts editing the specified for the specified row/column
39898      * @param {Number} rowIndex
39899      * @param {Number} colIndex
39900      */
39901     startEditing : function(row, col){
39902         this.stopEditing();
39903         if(this.colModel.isCellEditable(col, row)){
39904             this.view.ensureVisible(row, col, true);
39905           
39906             var r = this.dataSource.getAt(row);
39907             var field = this.colModel.getDataIndex(col);
39908             var cell = Roo.get(this.view.getCell(row,col));
39909             var e = {
39910                 grid: this,
39911                 record: r,
39912                 field: field,
39913                 value: r.data[field],
39914                 row: row,
39915                 column: col,
39916                 cancel:false 
39917             };
39918             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
39919                 this.editing = true;
39920                 var ed = this.colModel.getCellEditor(col, row);
39921                 
39922                 if (!ed) {
39923                     return;
39924                 }
39925                 if(!ed.rendered){
39926                     ed.render(ed.parentEl || document.body);
39927                 }
39928                 ed.field.reset();
39929                
39930                 cell.hide();
39931                 
39932                 (function(){ // complex but required for focus issues in safari, ie and opera
39933                     ed.row = row;
39934                     ed.col = col;
39935                     ed.record = r;
39936                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
39937                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
39938                     this.activeEditor = ed;
39939                     var v = r.data[field];
39940                     ed.startEdit(this.view.getCell(row, col), v);
39941                     // combo's with 'displayField and name set
39942                     if (ed.field.displayField && ed.field.name) {
39943                         ed.field.el.dom.value = r.data[ed.field.name];
39944                     }
39945                     
39946                     
39947                 }).defer(50, this);
39948             }
39949         }
39950     },
39951         
39952     /**
39953      * Stops any active editing
39954      */
39955     stopEditing : function(){
39956         if(this.activeEditor){
39957             this.activeEditor.completeEdit();
39958         }
39959         this.activeEditor = null;
39960     },
39961         
39962          /**
39963      * Called to get grid's drag proxy text, by default returns this.ddText.
39964      * @return {String}
39965      */
39966     getDragDropText : function(){
39967         var count = this.selModel.getSelectedCell() ? 1 : 0;
39968         return String.format(this.ddText, count, count == 1 ? '' : 's');
39969     }
39970         
39971 });/*
39972  * Based on:
39973  * Ext JS Library 1.1.1
39974  * Copyright(c) 2006-2007, Ext JS, LLC.
39975  *
39976  * Originally Released Under LGPL - original licence link has changed is not relivant.
39977  *
39978  * Fork - LGPL
39979  * <script type="text/javascript">
39980  */
39981
39982 // private - not really -- you end up using it !
39983 // This is a support class used internally by the Grid components
39984
39985 /**
39986  * @class Roo.grid.GridEditor
39987  * @extends Roo.Editor
39988  * Class for creating and editable grid elements.
39989  * @param {Object} config any settings (must include field)
39990  */
39991 Roo.grid.GridEditor = function(field, config){
39992     if (!config && field.field) {
39993         config = field;
39994         field = Roo.factory(config.field, Roo.form);
39995     }
39996     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
39997     field.monitorTab = false;
39998 };
39999
40000 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
40001     
40002     /**
40003      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
40004      */
40005     
40006     alignment: "tl-tl",
40007     autoSize: "width",
40008     hideEl : false,
40009     cls: "x-small-editor x-grid-editor",
40010     shim:false,
40011     shadow:"frame"
40012 });/*
40013  * Based on:
40014  * Ext JS Library 1.1.1
40015  * Copyright(c) 2006-2007, Ext JS, LLC.
40016  *
40017  * Originally Released Under LGPL - original licence link has changed is not relivant.
40018  *
40019  * Fork - LGPL
40020  * <script type="text/javascript">
40021  */
40022   
40023
40024   
40025 Roo.grid.PropertyRecord = Roo.data.Record.create([
40026     {name:'name',type:'string'},  'value'
40027 ]);
40028
40029
40030 Roo.grid.PropertyStore = function(grid, source){
40031     this.grid = grid;
40032     this.store = new Roo.data.Store({
40033         recordType : Roo.grid.PropertyRecord
40034     });
40035     this.store.on('update', this.onUpdate,  this);
40036     if(source){
40037         this.setSource(source);
40038     }
40039     Roo.grid.PropertyStore.superclass.constructor.call(this);
40040 };
40041
40042
40043
40044 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
40045     setSource : function(o){
40046         this.source = o;
40047         this.store.removeAll();
40048         var data = [];
40049         for(var k in o){
40050             if(this.isEditableValue(o[k])){
40051                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
40052             }
40053         }
40054         this.store.loadRecords({records: data}, {}, true);
40055     },
40056
40057     onUpdate : function(ds, record, type){
40058         if(type == Roo.data.Record.EDIT){
40059             var v = record.data['value'];
40060             var oldValue = record.modified['value'];
40061             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
40062                 this.source[record.id] = v;
40063                 record.commit();
40064                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
40065             }else{
40066                 record.reject();
40067             }
40068         }
40069     },
40070
40071     getProperty : function(row){
40072        return this.store.getAt(row);
40073     },
40074
40075     isEditableValue: function(val){
40076         if(val && val instanceof Date){
40077             return true;
40078         }else if(typeof val == 'object' || typeof val == 'function'){
40079             return false;
40080         }
40081         return true;
40082     },
40083
40084     setValue : function(prop, value){
40085         this.source[prop] = value;
40086         this.store.getById(prop).set('value', value);
40087     },
40088
40089     getSource : function(){
40090         return this.source;
40091     }
40092 });
40093
40094 Roo.grid.PropertyColumnModel = function(grid, store){
40095     this.grid = grid;
40096     var g = Roo.grid;
40097     g.PropertyColumnModel.superclass.constructor.call(this, [
40098         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
40099         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
40100     ]);
40101     this.store = store;
40102     this.bselect = Roo.DomHelper.append(document.body, {
40103         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
40104             {tag: 'option', value: 'true', html: 'true'},
40105             {tag: 'option', value: 'false', html: 'false'}
40106         ]
40107     });
40108     Roo.id(this.bselect);
40109     var f = Roo.form;
40110     this.editors = {
40111         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
40112         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
40113         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
40114         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
40115         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
40116     };
40117     this.renderCellDelegate = this.renderCell.createDelegate(this);
40118     this.renderPropDelegate = this.renderProp.createDelegate(this);
40119 };
40120
40121 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
40122     
40123     
40124     nameText : 'Name',
40125     valueText : 'Value',
40126     
40127     dateFormat : 'm/j/Y',
40128     
40129     
40130     renderDate : function(dateVal){
40131         return dateVal.dateFormat(this.dateFormat);
40132     },
40133
40134     renderBool : function(bVal){
40135         return bVal ? 'true' : 'false';
40136     },
40137
40138     isCellEditable : function(colIndex, rowIndex){
40139         return colIndex == 1;
40140     },
40141
40142     getRenderer : function(col){
40143         return col == 1 ?
40144             this.renderCellDelegate : this.renderPropDelegate;
40145     },
40146
40147     renderProp : function(v){
40148         return this.getPropertyName(v);
40149     },
40150
40151     renderCell : function(val){
40152         var rv = val;
40153         if(val instanceof Date){
40154             rv = this.renderDate(val);
40155         }else if(typeof val == 'boolean'){
40156             rv = this.renderBool(val);
40157         }
40158         return Roo.util.Format.htmlEncode(rv);
40159     },
40160
40161     getPropertyName : function(name){
40162         var pn = this.grid.propertyNames;
40163         return pn && pn[name] ? pn[name] : name;
40164     },
40165
40166     getCellEditor : function(colIndex, rowIndex){
40167         var p = this.store.getProperty(rowIndex);
40168         var n = p.data['name'], val = p.data['value'];
40169         
40170         if(typeof(this.grid.customEditors[n]) == 'string'){
40171             return this.editors[this.grid.customEditors[n]];
40172         }
40173         if(typeof(this.grid.customEditors[n]) != 'undefined'){
40174             return this.grid.customEditors[n];
40175         }
40176         if(val instanceof Date){
40177             return this.editors['date'];
40178         }else if(typeof val == 'number'){
40179             return this.editors['number'];
40180         }else if(typeof val == 'boolean'){
40181             return this.editors['boolean'];
40182         }else{
40183             return this.editors['string'];
40184         }
40185     }
40186 });
40187
40188 /**
40189  * @class Roo.grid.PropertyGrid
40190  * @extends Roo.grid.EditorGrid
40191  * This class represents the  interface of a component based property grid control.
40192  * <br><br>Usage:<pre><code>
40193  var grid = new Roo.grid.PropertyGrid("my-container-id", {
40194       
40195  });
40196  // set any options
40197  grid.render();
40198  * </code></pre>
40199   
40200  * @constructor
40201  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40202  * The container MUST have some type of size defined for the grid to fill. The container will be
40203  * automatically set to position relative if it isn't already.
40204  * @param {Object} config A config object that sets properties on this grid.
40205  */
40206 Roo.grid.PropertyGrid = function(container, config){
40207     config = config || {};
40208     var store = new Roo.grid.PropertyStore(this);
40209     this.store = store;
40210     var cm = new Roo.grid.PropertyColumnModel(this, store);
40211     store.store.sort('name', 'ASC');
40212     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
40213         ds: store.store,
40214         cm: cm,
40215         enableColLock:false,
40216         enableColumnMove:false,
40217         stripeRows:false,
40218         trackMouseOver: false,
40219         clicksToEdit:1
40220     }, config));
40221     this.getGridEl().addClass('x-props-grid');
40222     this.lastEditRow = null;
40223     this.on('columnresize', this.onColumnResize, this);
40224     this.addEvents({
40225          /**
40226              * @event beforepropertychange
40227              * Fires before a property changes (return false to stop?)
40228              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40229              * @param {String} id Record Id
40230              * @param {String} newval New Value
40231          * @param {String} oldval Old Value
40232              */
40233         "beforepropertychange": true,
40234         /**
40235              * @event propertychange
40236              * Fires after a property changes
40237              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40238              * @param {String} id Record Id
40239              * @param {String} newval New Value
40240          * @param {String} oldval Old Value
40241              */
40242         "propertychange": true
40243     });
40244     this.customEditors = this.customEditors || {};
40245 };
40246 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
40247     
40248      /**
40249      * @cfg {Object} customEditors map of colnames=> custom editors.
40250      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
40251      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
40252      * false disables editing of the field.
40253          */
40254     
40255       /**
40256      * @cfg {Object} propertyNames map of property Names to their displayed value
40257          */
40258     
40259     render : function(){
40260         Roo.grid.PropertyGrid.superclass.render.call(this);
40261         this.autoSize.defer(100, this);
40262     },
40263
40264     autoSize : function(){
40265         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
40266         if(this.view){
40267             this.view.fitColumns();
40268         }
40269     },
40270
40271     onColumnResize : function(){
40272         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
40273         this.autoSize();
40274     },
40275     /**
40276      * Sets the data for the Grid
40277      * accepts a Key => Value object of all the elements avaiable.
40278      * @param {Object} data  to appear in grid.
40279      */
40280     setSource : function(source){
40281         this.store.setSource(source);
40282         //this.autoSize();
40283     },
40284     /**
40285      * Gets all the data from the grid.
40286      * @return {Object} data  data stored in grid
40287      */
40288     getSource : function(){
40289         return this.store.getSource();
40290     }
40291 });/*
40292   
40293  * Licence LGPL
40294  
40295  */
40296  
40297 /**
40298  * @class Roo.grid.Calendar
40299  * @extends Roo.util.Grid
40300  * This class extends the Grid to provide a calendar widget
40301  * <br><br>Usage:<pre><code>
40302  var grid = new Roo.grid.Calendar("my-container-id", {
40303      ds: myDataStore,
40304      cm: myColModel,
40305      selModel: mySelectionModel,
40306      autoSizeColumns: true,
40307      monitorWindowResize: false,
40308      trackMouseOver: true
40309      eventstore : real data store..
40310  });
40311  // set any options
40312  grid.render();
40313   
40314   * @constructor
40315  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40316  * The container MUST have some type of size defined for the grid to fill. The container will be
40317  * automatically set to position relative if it isn't already.
40318  * @param {Object} config A config object that sets properties on this grid.
40319  */
40320 Roo.grid.Calendar = function(container, config){
40321         // initialize the container
40322         this.container = Roo.get(container);
40323         this.container.update("");
40324         this.container.setStyle("overflow", "hidden");
40325     this.container.addClass('x-grid-container');
40326
40327     this.id = this.container.id;
40328
40329     Roo.apply(this, config);
40330     // check and correct shorthanded configs
40331     
40332     var rows = [];
40333     var d =1;
40334     for (var r = 0;r < 6;r++) {
40335         
40336         rows[r]=[];
40337         for (var c =0;c < 7;c++) {
40338             rows[r][c]= '';
40339         }
40340     }
40341     if (this.eventStore) {
40342         this.eventStore= Roo.factory(this.eventStore, Roo.data);
40343         this.eventStore.on('load',this.onLoad, this);
40344         this.eventStore.on('beforeload',this.clearEvents, this);
40345          
40346     }
40347     
40348     this.dataSource = new Roo.data.Store({
40349             proxy: new Roo.data.MemoryProxy(rows),
40350             reader: new Roo.data.ArrayReader({}, [
40351                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
40352     });
40353
40354     this.dataSource.load();
40355     this.ds = this.dataSource;
40356     this.ds.xmodule = this.xmodule || false;
40357     
40358     
40359     var cellRender = function(v,x,r)
40360     {
40361         return String.format(
40362             '<div class="fc-day  fc-widget-content"><div>' +
40363                 '<div class="fc-event-container"></div>' +
40364                 '<div class="fc-day-number">{0}</div>'+
40365                 
40366                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
40367             '</div></div>', v);
40368     
40369     }
40370     
40371     
40372     this.colModel = new Roo.grid.ColumnModel( [
40373         {
40374             xtype: 'ColumnModel',
40375             xns: Roo.grid,
40376             dataIndex : 'weekday0',
40377             header : 'Sunday',
40378             renderer : cellRender
40379         },
40380         {
40381             xtype: 'ColumnModel',
40382             xns: Roo.grid,
40383             dataIndex : 'weekday1',
40384             header : 'Monday',
40385             renderer : cellRender
40386         },
40387         {
40388             xtype: 'ColumnModel',
40389             xns: Roo.grid,
40390             dataIndex : 'weekday2',
40391             header : 'Tuesday',
40392             renderer : cellRender
40393         },
40394         {
40395             xtype: 'ColumnModel',
40396             xns: Roo.grid,
40397             dataIndex : 'weekday3',
40398             header : 'Wednesday',
40399             renderer : cellRender
40400         },
40401         {
40402             xtype: 'ColumnModel',
40403             xns: Roo.grid,
40404             dataIndex : 'weekday4',
40405             header : 'Thursday',
40406             renderer : cellRender
40407         },
40408         {
40409             xtype: 'ColumnModel',
40410             xns: Roo.grid,
40411             dataIndex : 'weekday5',
40412             header : 'Friday',
40413             renderer : cellRender
40414         },
40415         {
40416             xtype: 'ColumnModel',
40417             xns: Roo.grid,
40418             dataIndex : 'weekday6',
40419             header : 'Saturday',
40420             renderer : cellRender
40421         }
40422     ]);
40423     this.cm = this.colModel;
40424     this.cm.xmodule = this.xmodule || false;
40425  
40426         
40427           
40428     //this.selModel = new Roo.grid.CellSelectionModel();
40429     //this.sm = this.selModel;
40430     //this.selModel.init(this);
40431     
40432     
40433     if(this.width){
40434         this.container.setWidth(this.width);
40435     }
40436
40437     if(this.height){
40438         this.container.setHeight(this.height);
40439     }
40440     /** @private */
40441         this.addEvents({
40442         // raw events
40443         /**
40444          * @event click
40445          * The raw click event for the entire grid.
40446          * @param {Roo.EventObject} e
40447          */
40448         "click" : true,
40449         /**
40450          * @event dblclick
40451          * The raw dblclick event for the entire grid.
40452          * @param {Roo.EventObject} e
40453          */
40454         "dblclick" : true,
40455         /**
40456          * @event contextmenu
40457          * The raw contextmenu event for the entire grid.
40458          * @param {Roo.EventObject} e
40459          */
40460         "contextmenu" : true,
40461         /**
40462          * @event mousedown
40463          * The raw mousedown event for the entire grid.
40464          * @param {Roo.EventObject} e
40465          */
40466         "mousedown" : true,
40467         /**
40468          * @event mouseup
40469          * The raw mouseup event for the entire grid.
40470          * @param {Roo.EventObject} e
40471          */
40472         "mouseup" : true,
40473         /**
40474          * @event mouseover
40475          * The raw mouseover event for the entire grid.
40476          * @param {Roo.EventObject} e
40477          */
40478         "mouseover" : true,
40479         /**
40480          * @event mouseout
40481          * The raw mouseout event for the entire grid.
40482          * @param {Roo.EventObject} e
40483          */
40484         "mouseout" : true,
40485         /**
40486          * @event keypress
40487          * The raw keypress event for the entire grid.
40488          * @param {Roo.EventObject} e
40489          */
40490         "keypress" : true,
40491         /**
40492          * @event keydown
40493          * The raw keydown event for the entire grid.
40494          * @param {Roo.EventObject} e
40495          */
40496         "keydown" : true,
40497
40498         // custom events
40499
40500         /**
40501          * @event cellclick
40502          * Fires when a cell is clicked
40503          * @param {Grid} this
40504          * @param {Number} rowIndex
40505          * @param {Number} columnIndex
40506          * @param {Roo.EventObject} e
40507          */
40508         "cellclick" : true,
40509         /**
40510          * @event celldblclick
40511          * Fires when a cell is double clicked
40512          * @param {Grid} this
40513          * @param {Number} rowIndex
40514          * @param {Number} columnIndex
40515          * @param {Roo.EventObject} e
40516          */
40517         "celldblclick" : true,
40518         /**
40519          * @event rowclick
40520          * Fires when a row is clicked
40521          * @param {Grid} this
40522          * @param {Number} rowIndex
40523          * @param {Roo.EventObject} e
40524          */
40525         "rowclick" : true,
40526         /**
40527          * @event rowdblclick
40528          * Fires when a row is double clicked
40529          * @param {Grid} this
40530          * @param {Number} rowIndex
40531          * @param {Roo.EventObject} e
40532          */
40533         "rowdblclick" : true,
40534         /**
40535          * @event headerclick
40536          * Fires when a header is clicked
40537          * @param {Grid} this
40538          * @param {Number} columnIndex
40539          * @param {Roo.EventObject} e
40540          */
40541         "headerclick" : true,
40542         /**
40543          * @event headerdblclick
40544          * Fires when a header cell is double clicked
40545          * @param {Grid} this
40546          * @param {Number} columnIndex
40547          * @param {Roo.EventObject} e
40548          */
40549         "headerdblclick" : true,
40550         /**
40551          * @event rowcontextmenu
40552          * Fires when a row is right clicked
40553          * @param {Grid} this
40554          * @param {Number} rowIndex
40555          * @param {Roo.EventObject} e
40556          */
40557         "rowcontextmenu" : true,
40558         /**
40559          * @event cellcontextmenu
40560          * Fires when a cell is right clicked
40561          * @param {Grid} this
40562          * @param {Number} rowIndex
40563          * @param {Number} cellIndex
40564          * @param {Roo.EventObject} e
40565          */
40566          "cellcontextmenu" : true,
40567         /**
40568          * @event headercontextmenu
40569          * Fires when a header is right clicked
40570          * @param {Grid} this
40571          * @param {Number} columnIndex
40572          * @param {Roo.EventObject} e
40573          */
40574         "headercontextmenu" : true,
40575         /**
40576          * @event bodyscroll
40577          * Fires when the body element is scrolled
40578          * @param {Number} scrollLeft
40579          * @param {Number} scrollTop
40580          */
40581         "bodyscroll" : true,
40582         /**
40583          * @event columnresize
40584          * Fires when the user resizes a column
40585          * @param {Number} columnIndex
40586          * @param {Number} newSize
40587          */
40588         "columnresize" : true,
40589         /**
40590          * @event columnmove
40591          * Fires when the user moves a column
40592          * @param {Number} oldIndex
40593          * @param {Number} newIndex
40594          */
40595         "columnmove" : true,
40596         /**
40597          * @event startdrag
40598          * Fires when row(s) start being dragged
40599          * @param {Grid} this
40600          * @param {Roo.GridDD} dd The drag drop object
40601          * @param {event} e The raw browser event
40602          */
40603         "startdrag" : true,
40604         /**
40605          * @event enddrag
40606          * Fires when a drag operation is complete
40607          * @param {Grid} this
40608          * @param {Roo.GridDD} dd The drag drop object
40609          * @param {event} e The raw browser event
40610          */
40611         "enddrag" : true,
40612         /**
40613          * @event dragdrop
40614          * Fires when dragged row(s) are dropped on a valid DD target
40615          * @param {Grid} this
40616          * @param {Roo.GridDD} dd The drag drop object
40617          * @param {String} targetId The target drag drop object
40618          * @param {event} e The raw browser event
40619          */
40620         "dragdrop" : true,
40621         /**
40622          * @event dragover
40623          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
40624          * @param {Grid} this
40625          * @param {Roo.GridDD} dd The drag drop object
40626          * @param {String} targetId The target drag drop object
40627          * @param {event} e The raw browser event
40628          */
40629         "dragover" : true,
40630         /**
40631          * @event dragenter
40632          *  Fires when the dragged row(s) first cross another DD target while being dragged
40633          * @param {Grid} this
40634          * @param {Roo.GridDD} dd The drag drop object
40635          * @param {String} targetId The target drag drop object
40636          * @param {event} e The raw browser event
40637          */
40638         "dragenter" : true,
40639         /**
40640          * @event dragout
40641          * Fires when the dragged row(s) leave another DD target while being dragged
40642          * @param {Grid} this
40643          * @param {Roo.GridDD} dd The drag drop object
40644          * @param {String} targetId The target drag drop object
40645          * @param {event} e The raw browser event
40646          */
40647         "dragout" : true,
40648         /**
40649          * @event rowclass
40650          * Fires when a row is rendered, so you can change add a style to it.
40651          * @param {GridView} gridview   The grid view
40652          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
40653          */
40654         'rowclass' : true,
40655
40656         /**
40657          * @event render
40658          * Fires when the grid is rendered
40659          * @param {Grid} grid
40660          */
40661         'render' : true,
40662             /**
40663              * @event select
40664              * Fires when a date is selected
40665              * @param {DatePicker} this
40666              * @param {Date} date The selected date
40667              */
40668         'select': true,
40669         /**
40670              * @event monthchange
40671              * Fires when the displayed month changes 
40672              * @param {DatePicker} this
40673              * @param {Date} date The selected month
40674              */
40675         'monthchange': true,
40676         /**
40677              * @event evententer
40678              * Fires when mouse over an event
40679              * @param {Calendar} this
40680              * @param {event} Event
40681              */
40682         'evententer': true,
40683         /**
40684              * @event eventleave
40685              * Fires when the mouse leaves an
40686              * @param {Calendar} this
40687              * @param {event}
40688              */
40689         'eventleave': true,
40690         /**
40691              * @event eventclick
40692              * Fires when the mouse click an
40693              * @param {Calendar} this
40694              * @param {event}
40695              */
40696         'eventclick': true,
40697         /**
40698              * @event eventrender
40699              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
40700              * @param {Calendar} this
40701              * @param {data} data to be modified
40702              */
40703         'eventrender': true
40704         
40705     });
40706
40707     Roo.grid.Grid.superclass.constructor.call(this);
40708     this.on('render', function() {
40709         this.view.el.addClass('x-grid-cal'); 
40710         
40711         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
40712
40713     },this);
40714     
40715     if (!Roo.grid.Calendar.style) {
40716         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
40717             
40718             
40719             '.x-grid-cal .x-grid-col' :  {
40720                 height: 'auto !important',
40721                 'vertical-align': 'top'
40722             },
40723             '.x-grid-cal  .fc-event-hori' : {
40724                 height: '14px'
40725             }
40726              
40727             
40728         }, Roo.id());
40729     }
40730
40731     
40732     
40733 };
40734 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
40735     /**
40736      * @cfg {Store} eventStore The store that loads events.
40737      */
40738     eventStore : 25,
40739
40740      
40741     activeDate : false,
40742     startDay : 0,
40743     autoWidth : true,
40744     monitorWindowResize : false,
40745
40746     
40747     resizeColumns : function() {
40748         var col = (this.view.el.getWidth() / 7) - 3;
40749         // loop through cols, and setWidth
40750         for(var i =0 ; i < 7 ; i++){
40751             this.cm.setColumnWidth(i, col);
40752         }
40753     },
40754      setDate :function(date) {
40755         
40756         Roo.log('setDate?');
40757         
40758         this.resizeColumns();
40759         var vd = this.activeDate;
40760         this.activeDate = date;
40761 //        if(vd && this.el){
40762 //            var t = date.getTime();
40763 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
40764 //                Roo.log('using add remove');
40765 //                
40766 //                this.fireEvent('monthchange', this, date);
40767 //                
40768 //                this.cells.removeClass("fc-state-highlight");
40769 //                this.cells.each(function(c){
40770 //                   if(c.dateValue == t){
40771 //                       c.addClass("fc-state-highlight");
40772 //                       setTimeout(function(){
40773 //                            try{c.dom.firstChild.focus();}catch(e){}
40774 //                       }, 50);
40775 //                       return false;
40776 //                   }
40777 //                   return true;
40778 //                });
40779 //                return;
40780 //            }
40781 //        }
40782         
40783         var days = date.getDaysInMonth();
40784         
40785         var firstOfMonth = date.getFirstDateOfMonth();
40786         var startingPos = firstOfMonth.getDay()-this.startDay;
40787         
40788         if(startingPos < this.startDay){
40789             startingPos += 7;
40790         }
40791         
40792         var pm = date.add(Date.MONTH, -1);
40793         var prevStart = pm.getDaysInMonth()-startingPos;
40794 //        
40795         
40796         
40797         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
40798         
40799         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
40800         //this.cells.addClassOnOver('fc-state-hover');
40801         
40802         var cells = this.cells.elements;
40803         var textEls = this.textNodes;
40804         
40805         //Roo.each(cells, function(cell){
40806         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
40807         //});
40808         
40809         days += startingPos;
40810
40811         // convert everything to numbers so it's fast
40812         var day = 86400000;
40813         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
40814         //Roo.log(d);
40815         //Roo.log(pm);
40816         //Roo.log(prevStart);
40817         
40818         var today = new Date().clearTime().getTime();
40819         var sel = date.clearTime().getTime();
40820         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
40821         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
40822         var ddMatch = this.disabledDatesRE;
40823         var ddText = this.disabledDatesText;
40824         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
40825         var ddaysText = this.disabledDaysText;
40826         var format = this.format;
40827         
40828         var setCellClass = function(cal, cell){
40829             
40830             //Roo.log('set Cell Class');
40831             cell.title = "";
40832             var t = d.getTime();
40833             
40834             //Roo.log(d);
40835             
40836             
40837             cell.dateValue = t;
40838             if(t == today){
40839                 cell.className += " fc-today";
40840                 cell.className += " fc-state-highlight";
40841                 cell.title = cal.todayText;
40842             }
40843             if(t == sel){
40844                 // disable highlight in other month..
40845                 cell.className += " fc-state-highlight";
40846                 
40847             }
40848             // disabling
40849             if(t < min) {
40850                 //cell.className = " fc-state-disabled";
40851                 cell.title = cal.minText;
40852                 return;
40853             }
40854             if(t > max) {
40855                 //cell.className = " fc-state-disabled";
40856                 cell.title = cal.maxText;
40857                 return;
40858             }
40859             if(ddays){
40860                 if(ddays.indexOf(d.getDay()) != -1){
40861                     // cell.title = ddaysText;
40862                    // cell.className = " fc-state-disabled";
40863                 }
40864             }
40865             if(ddMatch && format){
40866                 var fvalue = d.dateFormat(format);
40867                 if(ddMatch.test(fvalue)){
40868                     cell.title = ddText.replace("%0", fvalue);
40869                    cell.className = " fc-state-disabled";
40870                 }
40871             }
40872             
40873             if (!cell.initialClassName) {
40874                 cell.initialClassName = cell.dom.className;
40875             }
40876             
40877             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
40878         };
40879
40880         var i = 0;
40881         
40882         for(; i < startingPos; i++) {
40883             cells[i].dayName =  (++prevStart);
40884             Roo.log(textEls[i]);
40885             d.setDate(d.getDate()+1);
40886             
40887             //cells[i].className = "fc-past fc-other-month";
40888             setCellClass(this, cells[i]);
40889         }
40890         
40891         var intDay = 0;
40892         
40893         for(; i < days; i++){
40894             intDay = i - startingPos + 1;
40895             cells[i].dayName =  (intDay);
40896             d.setDate(d.getDate()+1);
40897             
40898             cells[i].className = ''; // "x-date-active";
40899             setCellClass(this, cells[i]);
40900         }
40901         var extraDays = 0;
40902         
40903         for(; i < 42; i++) {
40904             //textEls[i].innerHTML = (++extraDays);
40905             
40906             d.setDate(d.getDate()+1);
40907             cells[i].dayName = (++extraDays);
40908             cells[i].className = "fc-future fc-other-month";
40909             setCellClass(this, cells[i]);
40910         }
40911         
40912         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
40913         
40914         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
40915         
40916         // this will cause all the cells to mis
40917         var rows= [];
40918         var i =0;
40919         for (var r = 0;r < 6;r++) {
40920             for (var c =0;c < 7;c++) {
40921                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
40922             }    
40923         }
40924         
40925         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
40926         for(i=0;i<cells.length;i++) {
40927             
40928             this.cells.elements[i].dayName = cells[i].dayName ;
40929             this.cells.elements[i].className = cells[i].className;
40930             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
40931             this.cells.elements[i].title = cells[i].title ;
40932             this.cells.elements[i].dateValue = cells[i].dateValue ;
40933         }
40934         
40935         
40936         
40937         
40938         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
40939         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
40940         
40941         ////if(totalRows != 6){
40942             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
40943            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
40944        // }
40945         
40946         this.fireEvent('monthchange', this, date);
40947         
40948         
40949     },
40950  /**
40951      * Returns the grid's SelectionModel.
40952      * @return {SelectionModel}
40953      */
40954     getSelectionModel : function(){
40955         if(!this.selModel){
40956             this.selModel = new Roo.grid.CellSelectionModel();
40957         }
40958         return this.selModel;
40959     },
40960
40961     load: function() {
40962         this.eventStore.load()
40963         
40964         
40965         
40966     },
40967     
40968     findCell : function(dt) {
40969         dt = dt.clearTime().getTime();
40970         var ret = false;
40971         this.cells.each(function(c){
40972             //Roo.log("check " +c.dateValue + '?=' + dt);
40973             if(c.dateValue == dt){
40974                 ret = c;
40975                 return false;
40976             }
40977             return true;
40978         });
40979         
40980         return ret;
40981     },
40982     
40983     findCells : function(rec) {
40984         var s = rec.data.start_dt.clone().clearTime().getTime();
40985        // Roo.log(s);
40986         var e= rec.data.end_dt.clone().clearTime().getTime();
40987        // Roo.log(e);
40988         var ret = [];
40989         this.cells.each(function(c){
40990              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
40991             
40992             if(c.dateValue > e){
40993                 return ;
40994             }
40995             if(c.dateValue < s){
40996                 return ;
40997             }
40998             ret.push(c);
40999         });
41000         
41001         return ret;    
41002     },
41003     
41004     findBestRow: function(cells)
41005     {
41006         var ret = 0;
41007         
41008         for (var i =0 ; i < cells.length;i++) {
41009             ret  = Math.max(cells[i].rows || 0,ret);
41010         }
41011         return ret;
41012         
41013     },
41014     
41015     
41016     addItem : function(rec)
41017     {
41018         // look for vertical location slot in
41019         var cells = this.findCells(rec);
41020         
41021         rec.row = this.findBestRow(cells);
41022         
41023         // work out the location.
41024         
41025         var crow = false;
41026         var rows = [];
41027         for(var i =0; i < cells.length; i++) {
41028             if (!crow) {
41029                 crow = {
41030                     start : cells[i],
41031                     end :  cells[i]
41032                 };
41033                 continue;
41034             }
41035             if (crow.start.getY() == cells[i].getY()) {
41036                 // on same row.
41037                 crow.end = cells[i];
41038                 continue;
41039             }
41040             // different row.
41041             rows.push(crow);
41042             crow = {
41043                 start: cells[i],
41044                 end : cells[i]
41045             };
41046             
41047         }
41048         
41049         rows.push(crow);
41050         rec.els = [];
41051         rec.rows = rows;
41052         rec.cells = cells;
41053         for (var i = 0; i < cells.length;i++) {
41054             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
41055             
41056         }
41057         
41058         
41059     },
41060     
41061     clearEvents: function() {
41062         
41063         if (!this.eventStore.getCount()) {
41064             return;
41065         }
41066         // reset number of rows in cells.
41067         Roo.each(this.cells.elements, function(c){
41068             c.rows = 0;
41069         });
41070         
41071         this.eventStore.each(function(e) {
41072             this.clearEvent(e);
41073         },this);
41074         
41075     },
41076     
41077     clearEvent : function(ev)
41078     {
41079         if (ev.els) {
41080             Roo.each(ev.els, function(el) {
41081                 el.un('mouseenter' ,this.onEventEnter, this);
41082                 el.un('mouseleave' ,this.onEventLeave, this);
41083                 el.remove();
41084             },this);
41085             ev.els = [];
41086         }
41087     },
41088     
41089     
41090     renderEvent : function(ev,ctr) {
41091         if (!ctr) {
41092              ctr = this.view.el.select('.fc-event-container',true).first();
41093         }
41094         
41095          
41096         this.clearEvent(ev);
41097             //code
41098        
41099         
41100         
41101         ev.els = [];
41102         var cells = ev.cells;
41103         var rows = ev.rows;
41104         this.fireEvent('eventrender', this, ev);
41105         
41106         for(var i =0; i < rows.length; i++) {
41107             
41108             cls = '';
41109             if (i == 0) {
41110                 cls += ' fc-event-start';
41111             }
41112             if ((i+1) == rows.length) {
41113                 cls += ' fc-event-end';
41114             }
41115             
41116             //Roo.log(ev.data);
41117             // how many rows should it span..
41118             var cg = this.eventTmpl.append(ctr,Roo.apply({
41119                 fccls : cls
41120                 
41121             }, ev.data) , true);
41122             
41123             
41124             cg.on('mouseenter' ,this.onEventEnter, this, ev);
41125             cg.on('mouseleave' ,this.onEventLeave, this, ev);
41126             cg.on('click', this.onEventClick, this, ev);
41127             
41128             ev.els.push(cg);
41129             
41130             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
41131             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
41132             //Roo.log(cg);
41133              
41134             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
41135             cg.setWidth(ebox.right - sbox.x -2);
41136         }
41137     },
41138     
41139     renderEvents: function()
41140     {   
41141         // first make sure there is enough space..
41142         
41143         if (!this.eventTmpl) {
41144             this.eventTmpl = new Roo.Template(
41145                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
41146                     '<div class="fc-event-inner">' +
41147                         '<span class="fc-event-time">{time}</span>' +
41148                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
41149                     '</div>' +
41150                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
41151                 '</div>'
41152             );
41153                 
41154         }
41155                
41156         
41157         
41158         this.cells.each(function(c) {
41159             //Roo.log(c.select('.fc-day-content div',true).first());
41160             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
41161         });
41162         
41163         var ctr = this.view.el.select('.fc-event-container',true).first();
41164         
41165         var cls;
41166         this.eventStore.each(function(ev){
41167             
41168             this.renderEvent(ev);
41169              
41170              
41171         }, this);
41172         this.view.layout();
41173         
41174     },
41175     
41176     onEventEnter: function (e, el,event,d) {
41177         this.fireEvent('evententer', this, el, event);
41178     },
41179     
41180     onEventLeave: function (e, el,event,d) {
41181         this.fireEvent('eventleave', this, el, event);
41182     },
41183     
41184     onEventClick: function (e, el,event,d) {
41185         this.fireEvent('eventclick', this, el, event);
41186     },
41187     
41188     onMonthChange: function () {
41189         this.store.load();
41190     },
41191     
41192     onLoad: function () {
41193         
41194         //Roo.log('calendar onload');
41195 //         
41196         if(this.eventStore.getCount() > 0){
41197             
41198            
41199             
41200             this.eventStore.each(function(d){
41201                 
41202                 
41203                 // FIXME..
41204                 var add =   d.data;
41205                 if (typeof(add.end_dt) == 'undefined')  {
41206                     Roo.log("Missing End time in calendar data: ");
41207                     Roo.log(d);
41208                     return;
41209                 }
41210                 if (typeof(add.start_dt) == 'undefined')  {
41211                     Roo.log("Missing Start time in calendar data: ");
41212                     Roo.log(d);
41213                     return;
41214                 }
41215                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
41216                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
41217                 add.id = add.id || d.id;
41218                 add.title = add.title || '??';
41219                 
41220                 this.addItem(d);
41221                 
41222              
41223             },this);
41224         }
41225         
41226         this.renderEvents();
41227     }
41228     
41229
41230 });
41231 /*
41232  grid : {
41233                 xtype: 'Grid',
41234                 xns: Roo.grid,
41235                 listeners : {
41236                     render : function ()
41237                     {
41238                         _this.grid = this;
41239                         
41240                         if (!this.view.el.hasClass('course-timesheet')) {
41241                             this.view.el.addClass('course-timesheet');
41242                         }
41243                         if (this.tsStyle) {
41244                             this.ds.load({});
41245                             return; 
41246                         }
41247                         Roo.log('width');
41248                         Roo.log(_this.grid.view.el.getWidth());
41249                         
41250                         
41251                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
41252                             '.course-timesheet .x-grid-row' : {
41253                                 height: '80px'
41254                             },
41255                             '.x-grid-row td' : {
41256                                 'vertical-align' : 0
41257                             },
41258                             '.course-edit-link' : {
41259                                 'color' : 'blue',
41260                                 'text-overflow' : 'ellipsis',
41261                                 'overflow' : 'hidden',
41262                                 'white-space' : 'nowrap',
41263                                 'cursor' : 'pointer'
41264                             },
41265                             '.sub-link' : {
41266                                 'color' : 'green'
41267                             },
41268                             '.de-act-sup-link' : {
41269                                 'color' : 'purple',
41270                                 'text-decoration' : 'line-through'
41271                             },
41272                             '.de-act-link' : {
41273                                 'color' : 'red',
41274                                 'text-decoration' : 'line-through'
41275                             },
41276                             '.course-timesheet .course-highlight' : {
41277                                 'border-top-style': 'dashed !important',
41278                                 'border-bottom-bottom': 'dashed !important'
41279                             },
41280                             '.course-timesheet .course-item' : {
41281                                 'font-family'   : 'tahoma, arial, helvetica',
41282                                 'font-size'     : '11px',
41283                                 'overflow'      : 'hidden',
41284                                 'padding-left'  : '10px',
41285                                 'padding-right' : '10px',
41286                                 'padding-top' : '10px' 
41287                             }
41288                             
41289                         }, Roo.id());
41290                                 this.ds.load({});
41291                     }
41292                 },
41293                 autoWidth : true,
41294                 monitorWindowResize : false,
41295                 cellrenderer : function(v,x,r)
41296                 {
41297                     return v;
41298                 },
41299                 sm : {
41300                     xtype: 'CellSelectionModel',
41301                     xns: Roo.grid
41302                 },
41303                 dataSource : {
41304                     xtype: 'Store',
41305                     xns: Roo.data,
41306                     listeners : {
41307                         beforeload : function (_self, options)
41308                         {
41309                             options.params = options.params || {};
41310                             options.params._month = _this.monthField.getValue();
41311                             options.params.limit = 9999;
41312                             options.params['sort'] = 'when_dt';    
41313                             options.params['dir'] = 'ASC';    
41314                             this.proxy.loadResponse = this.loadResponse;
41315                             Roo.log("load?");
41316                             //this.addColumns();
41317                         },
41318                         load : function (_self, records, options)
41319                         {
41320                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
41321                                 // if you click on the translation.. you can edit it...
41322                                 var el = Roo.get(this);
41323                                 var id = el.dom.getAttribute('data-id');
41324                                 var d = el.dom.getAttribute('data-date');
41325                                 var t = el.dom.getAttribute('data-time');
41326                                 //var id = this.child('span').dom.textContent;
41327                                 
41328                                 //Roo.log(this);
41329                                 Pman.Dialog.CourseCalendar.show({
41330                                     id : id,
41331                                     when_d : d,
41332                                     when_t : t,
41333                                     productitem_active : id ? 1 : 0
41334                                 }, function() {
41335                                     _this.grid.ds.load({});
41336                                 });
41337                            
41338                            });
41339                            
41340                            _this.panel.fireEvent('resize', [ '', '' ]);
41341                         }
41342                     },
41343                     loadResponse : function(o, success, response){
41344                             // this is overridden on before load..
41345                             
41346                             Roo.log("our code?");       
41347                             //Roo.log(success);
41348                             //Roo.log(response)
41349                             delete this.activeRequest;
41350                             if(!success){
41351                                 this.fireEvent("loadexception", this, o, response);
41352                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41353                                 return;
41354                             }
41355                             var result;
41356                             try {
41357                                 result = o.reader.read(response);
41358                             }catch(e){
41359                                 Roo.log("load exception?");
41360                                 this.fireEvent("loadexception", this, o, response, e);
41361                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41362                                 return;
41363                             }
41364                             Roo.log("ready...");        
41365                             // loop through result.records;
41366                             // and set this.tdate[date] = [] << array of records..
41367                             _this.tdata  = {};
41368                             Roo.each(result.records, function(r){
41369                                 //Roo.log(r.data);
41370                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
41371                                     _this.tdata[r.data.when_dt.format('j')] = [];
41372                                 }
41373                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
41374                             });
41375                             
41376                             //Roo.log(_this.tdata);
41377                             
41378                             result.records = [];
41379                             result.totalRecords = 6;
41380                     
41381                             // let's generate some duumy records for the rows.
41382                             //var st = _this.dateField.getValue();
41383                             
41384                             // work out monday..
41385                             //st = st.add(Date.DAY, -1 * st.format('w'));
41386                             
41387                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41388                             
41389                             var firstOfMonth = date.getFirstDayOfMonth();
41390                             var days = date.getDaysInMonth();
41391                             var d = 1;
41392                             var firstAdded = false;
41393                             for (var i = 0; i < result.totalRecords ; i++) {
41394                                 //var d= st.add(Date.DAY, i);
41395                                 var row = {};
41396                                 var added = 0;
41397                                 for(var w = 0 ; w < 7 ; w++){
41398                                     if(!firstAdded && firstOfMonth != w){
41399                                         continue;
41400                                     }
41401                                     if(d > days){
41402                                         continue;
41403                                     }
41404                                     firstAdded = true;
41405                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
41406                                     row['weekday'+w] = String.format(
41407                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
41408                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
41409                                                     d,
41410                                                     date.format('Y-m-')+dd
41411                                                 );
41412                                     added++;
41413                                     if(typeof(_this.tdata[d]) != 'undefined'){
41414                                         Roo.each(_this.tdata[d], function(r){
41415                                             var is_sub = '';
41416                                             var deactive = '';
41417                                             var id = r.id;
41418                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
41419                                             if(r.parent_id*1>0){
41420                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
41421                                                 id = r.parent_id;
41422                                             }
41423                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
41424                                                 deactive = 'de-act-link';
41425                                             }
41426                                             
41427                                             row['weekday'+w] += String.format(
41428                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
41429                                                     id, //0
41430                                                     r.product_id_name, //1
41431                                                     r.when_dt.format('h:ia'), //2
41432                                                     is_sub, //3
41433                                                     deactive, //4
41434                                                     desc // 5
41435                                             );
41436                                         });
41437                                     }
41438                                     d++;
41439                                 }
41440                                 
41441                                 // only do this if something added..
41442                                 if(added > 0){ 
41443                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
41444                                 }
41445                                 
41446                                 
41447                                 // push it twice. (second one with an hour..
41448                                 
41449                             }
41450                             //Roo.log(result);
41451                             this.fireEvent("load", this, o, o.request.arg);
41452                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
41453                         },
41454                     sortInfo : {field: 'when_dt', direction : 'ASC' },
41455                     proxy : {
41456                         xtype: 'HttpProxy',
41457                         xns: Roo.data,
41458                         method : 'GET',
41459                         url : baseURL + '/Roo/Shop_course.php'
41460                     },
41461                     reader : {
41462                         xtype: 'JsonReader',
41463                         xns: Roo.data,
41464                         id : 'id',
41465                         fields : [
41466                             {
41467                                 'name': 'id',
41468                                 'type': 'int'
41469                             },
41470                             {
41471                                 'name': 'when_dt',
41472                                 'type': 'string'
41473                             },
41474                             {
41475                                 'name': 'end_dt',
41476                                 'type': 'string'
41477                             },
41478                             {
41479                                 'name': 'parent_id',
41480                                 'type': 'int'
41481                             },
41482                             {
41483                                 'name': 'product_id',
41484                                 'type': 'int'
41485                             },
41486                             {
41487                                 'name': 'productitem_id',
41488                                 'type': 'int'
41489                             },
41490                             {
41491                                 'name': 'guid',
41492                                 'type': 'int'
41493                             }
41494                         ]
41495                     }
41496                 },
41497                 toolbar : {
41498                     xtype: 'Toolbar',
41499                     xns: Roo,
41500                     items : [
41501                         {
41502                             xtype: 'Button',
41503                             xns: Roo.Toolbar,
41504                             listeners : {
41505                                 click : function (_self, e)
41506                                 {
41507                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41508                                     sd.setMonth(sd.getMonth()-1);
41509                                     _this.monthField.setValue(sd.format('Y-m-d'));
41510                                     _this.grid.ds.load({});
41511                                 }
41512                             },
41513                             text : "Back"
41514                         },
41515                         {
41516                             xtype: 'Separator',
41517                             xns: Roo.Toolbar
41518                         },
41519                         {
41520                             xtype: 'MonthField',
41521                             xns: Roo.form,
41522                             listeners : {
41523                                 render : function (_self)
41524                                 {
41525                                     _this.monthField = _self;
41526                                    // _this.monthField.set  today
41527                                 },
41528                                 select : function (combo, date)
41529                                 {
41530                                     _this.grid.ds.load({});
41531                                 }
41532                             },
41533                             value : (function() { return new Date(); })()
41534                         },
41535                         {
41536                             xtype: 'Separator',
41537                             xns: Roo.Toolbar
41538                         },
41539                         {
41540                             xtype: 'TextItem',
41541                             xns: Roo.Toolbar,
41542                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
41543                         },
41544                         {
41545                             xtype: 'Fill',
41546                             xns: Roo.Toolbar
41547                         },
41548                         {
41549                             xtype: 'Button',
41550                             xns: Roo.Toolbar,
41551                             listeners : {
41552                                 click : function (_self, e)
41553                                 {
41554                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41555                                     sd.setMonth(sd.getMonth()+1);
41556                                     _this.monthField.setValue(sd.format('Y-m-d'));
41557                                     _this.grid.ds.load({});
41558                                 }
41559                             },
41560                             text : "Next"
41561                         }
41562                     ]
41563                 },
41564                  
41565             }
41566         };
41567         
41568         *//*
41569  * Based on:
41570  * Ext JS Library 1.1.1
41571  * Copyright(c) 2006-2007, Ext JS, LLC.
41572  *
41573  * Originally Released Under LGPL - original licence link has changed is not relivant.
41574  *
41575  * Fork - LGPL
41576  * <script type="text/javascript">
41577  */
41578  
41579 /**
41580  * @class Roo.LoadMask
41581  * A simple utility class for generically masking elements while loading data.  If the element being masked has
41582  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
41583  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
41584  * element's UpdateManager load indicator and will be destroyed after the initial load.
41585  * @constructor
41586  * Create a new LoadMask
41587  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
41588  * @param {Object} config The config object
41589  */
41590 Roo.LoadMask = function(el, config){
41591     this.el = Roo.get(el);
41592     Roo.apply(this, config);
41593     if(this.store){
41594         this.store.on('beforeload', this.onBeforeLoad, this);
41595         this.store.on('load', this.onLoad, this);
41596         this.store.on('loadexception', this.onLoadException, this);
41597         this.removeMask = false;
41598     }else{
41599         var um = this.el.getUpdateManager();
41600         um.showLoadIndicator = false; // disable the default indicator
41601         um.on('beforeupdate', this.onBeforeLoad, this);
41602         um.on('update', this.onLoad, this);
41603         um.on('failure', this.onLoad, this);
41604         this.removeMask = true;
41605     }
41606 };
41607
41608 Roo.LoadMask.prototype = {
41609     /**
41610      * @cfg {Boolean} removeMask
41611      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
41612      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
41613      */
41614     /**
41615      * @cfg {String} msg
41616      * The text to display in a centered loading message box (defaults to 'Loading...')
41617      */
41618     msg : 'Loading...',
41619     /**
41620      * @cfg {String} msgCls
41621      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
41622      */
41623     msgCls : 'x-mask-loading',
41624
41625     /**
41626      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
41627      * @type Boolean
41628      */
41629     disabled: false,
41630
41631     /**
41632      * Disables the mask to prevent it from being displayed
41633      */
41634     disable : function(){
41635        this.disabled = true;
41636     },
41637
41638     /**
41639      * Enables the mask so that it can be displayed
41640      */
41641     enable : function(){
41642         this.disabled = false;
41643     },
41644     
41645     onLoadException : function()
41646     {
41647         Roo.log(arguments);
41648         
41649         if (typeof(arguments[3]) != 'undefined') {
41650             Roo.MessageBox.alert("Error loading",arguments[3]);
41651         } 
41652         /*
41653         try {
41654             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41655                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41656             }   
41657         } catch(e) {
41658             
41659         }
41660         */
41661     
41662         
41663         
41664         this.el.unmask(this.removeMask);
41665     },
41666     // private
41667     onLoad : function()
41668     {
41669         this.el.unmask(this.removeMask);
41670     },
41671
41672     // private
41673     onBeforeLoad : function(){
41674         if(!this.disabled){
41675             this.el.mask(this.msg, this.msgCls);
41676         }
41677     },
41678
41679     // private
41680     destroy : function(){
41681         if(this.store){
41682             this.store.un('beforeload', this.onBeforeLoad, this);
41683             this.store.un('load', this.onLoad, this);
41684             this.store.un('loadexception', this.onLoadException, this);
41685         }else{
41686             var um = this.el.getUpdateManager();
41687             um.un('beforeupdate', this.onBeforeLoad, this);
41688             um.un('update', this.onLoad, this);
41689             um.un('failure', this.onLoad, this);
41690         }
41691     }
41692 };/*
41693  * Based on:
41694  * Ext JS Library 1.1.1
41695  * Copyright(c) 2006-2007, Ext JS, LLC.
41696  *
41697  * Originally Released Under LGPL - original licence link has changed is not relivant.
41698  *
41699  * Fork - LGPL
41700  * <script type="text/javascript">
41701  */
41702
41703
41704 /**
41705  * @class Roo.XTemplate
41706  * @extends Roo.Template
41707  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
41708 <pre><code>
41709 var t = new Roo.XTemplate(
41710         '&lt;select name="{name}"&gt;',
41711                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
41712         '&lt;/select&gt;'
41713 );
41714  
41715 // then append, applying the master template values
41716  </code></pre>
41717  *
41718  * Supported features:
41719  *
41720  *  Tags:
41721
41722 <pre><code>
41723       {a_variable} - output encoded.
41724       {a_variable.format:("Y-m-d")} - call a method on the variable
41725       {a_variable:raw} - unencoded output
41726       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
41727       {a_variable:this.method_on_template(...)} - call a method on the template object.
41728  
41729 </code></pre>
41730  *  The tpl tag:
41731 <pre><code>
41732         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
41733         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
41734         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
41735         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
41736   
41737         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
41738         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
41739 </code></pre>
41740  *      
41741  */
41742 Roo.XTemplate = function()
41743 {
41744     Roo.XTemplate.superclass.constructor.apply(this, arguments);
41745     if (this.html) {
41746         this.compile();
41747     }
41748 };
41749
41750
41751 Roo.extend(Roo.XTemplate, Roo.Template, {
41752
41753     /**
41754      * The various sub templates
41755      */
41756     tpls : false,
41757     /**
41758      *
41759      * basic tag replacing syntax
41760      * WORD:WORD()
41761      *
41762      * // you can fake an object call by doing this
41763      *  x.t:(test,tesT) 
41764      * 
41765      */
41766     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
41767
41768     /**
41769      * compile the template
41770      *
41771      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
41772      *
41773      */
41774     compile: function()
41775     {
41776         var s = this.html;
41777      
41778         s = ['<tpl>', s, '</tpl>'].join('');
41779     
41780         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
41781             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
41782             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
41783             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
41784             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
41785             m,
41786             id     = 0,
41787             tpls   = [];
41788     
41789         while(true == !!(m = s.match(re))){
41790             var forMatch   = m[0].match(nameRe),
41791                 ifMatch   = m[0].match(ifRe),
41792                 execMatch   = m[0].match(execRe),
41793                 namedMatch   = m[0].match(namedRe),
41794                 
41795                 exp  = null, 
41796                 fn   = null,
41797                 exec = null,
41798                 name = forMatch && forMatch[1] ? forMatch[1] : '';
41799                 
41800             if (ifMatch) {
41801                 // if - puts fn into test..
41802                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
41803                 if(exp){
41804                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
41805                 }
41806             }
41807             
41808             if (execMatch) {
41809                 // exec - calls a function... returns empty if true is  returned.
41810                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
41811                 if(exp){
41812                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
41813                 }
41814             }
41815             
41816             
41817             if (name) {
41818                 // for = 
41819                 switch(name){
41820                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
41821                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
41822                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
41823                 }
41824             }
41825             var uid = namedMatch ? namedMatch[1] : id;
41826             
41827             
41828             tpls.push({
41829                 id:     namedMatch ? namedMatch[1] : id,
41830                 target: name,
41831                 exec:   exec,
41832                 test:   fn,
41833                 body:   m[1] || ''
41834             });
41835             if (namedMatch) {
41836                 s = s.replace(m[0], '');
41837             } else { 
41838                 s = s.replace(m[0], '{xtpl'+ id + '}');
41839             }
41840             ++id;
41841         }
41842         this.tpls = [];
41843         for(var i = tpls.length-1; i >= 0; --i){
41844             this.compileTpl(tpls[i]);
41845             this.tpls[tpls[i].id] = tpls[i];
41846         }
41847         this.master = tpls[tpls.length-1];
41848         return this;
41849     },
41850     /**
41851      * same as applyTemplate, except it's done to one of the subTemplates
41852      * when using named templates, you can do:
41853      *
41854      * var str = pl.applySubTemplate('your-name', values);
41855      *
41856      * 
41857      * @param {Number} id of the template
41858      * @param {Object} values to apply to template
41859      * @param {Object} parent (normaly the instance of this object)
41860      */
41861     applySubTemplate : function(id, values, parent)
41862     {
41863         
41864         
41865         var t = this.tpls[id];
41866         
41867         
41868         try { 
41869             if(t.test && !t.test.call(this, values, parent)){
41870                 return '';
41871             }
41872         } catch(e) {
41873             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
41874             Roo.log(e.toString());
41875             Roo.log(t.test);
41876             return ''
41877         }
41878         try { 
41879             
41880             if(t.exec && t.exec.call(this, values, parent)){
41881                 return '';
41882             }
41883         } catch(e) {
41884             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
41885             Roo.log(e.toString());
41886             Roo.log(t.exec);
41887             return ''
41888         }
41889         try {
41890             var vs = t.target ? t.target.call(this, values, parent) : values;
41891             parent = t.target ? values : parent;
41892             if(t.target && vs instanceof Array){
41893                 var buf = [];
41894                 for(var i = 0, len = vs.length; i < len; i++){
41895                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
41896                 }
41897                 return buf.join('');
41898             }
41899             return t.compiled.call(this, vs, parent);
41900         } catch (e) {
41901             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
41902             Roo.log(e.toString());
41903             Roo.log(t.compiled);
41904             return '';
41905         }
41906     },
41907
41908     compileTpl : function(tpl)
41909     {
41910         var fm = Roo.util.Format;
41911         var useF = this.disableFormats !== true;
41912         var sep = Roo.isGecko ? "+" : ",";
41913         var undef = function(str) {
41914             Roo.log("Property not found :"  + str);
41915             return '';
41916         };
41917         
41918         var fn = function(m, name, format, args)
41919         {
41920             //Roo.log(arguments);
41921             args = args ? args.replace(/\\'/g,"'") : args;
41922             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
41923             if (typeof(format) == 'undefined') {
41924                 format= 'htmlEncode';
41925             }
41926             if (format == 'raw' ) {
41927                 format = false;
41928             }
41929             
41930             if(name.substr(0, 4) == 'xtpl'){
41931                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
41932             }
41933             
41934             // build an array of options to determine if value is undefined..
41935             
41936             // basically get 'xxxx.yyyy' then do
41937             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
41938             //    (function () { Roo.log("Property not found"); return ''; })() :
41939             //    ......
41940             
41941             var udef_ar = [];
41942             var lookfor = '';
41943             Roo.each(name.split('.'), function(st) {
41944                 lookfor += (lookfor.length ? '.': '') + st;
41945                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
41946             });
41947             
41948             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
41949             
41950             
41951             if(format && useF){
41952                 
41953                 args = args ? ',' + args : "";
41954                  
41955                 if(format.substr(0, 5) != "this."){
41956                     format = "fm." + format + '(';
41957                 }else{
41958                     format = 'this.call("'+ format.substr(5) + '", ';
41959                     args = ", values";
41960                 }
41961                 
41962                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
41963             }
41964              
41965             if (args.length) {
41966                 // called with xxyx.yuu:(test,test)
41967                 // change to ()
41968                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
41969             }
41970             // raw.. - :raw modifier..
41971             return "'"+ sep + udef_st  + name + ")"+sep+"'";
41972             
41973         };
41974         var body;
41975         // branched to use + in gecko and [].join() in others
41976         if(Roo.isGecko){
41977             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
41978                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
41979                     "';};};";
41980         }else{
41981             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
41982             body.push(tpl.body.replace(/(\r\n|\n)/g,
41983                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
41984             body.push("'].join('');};};");
41985             body = body.join('');
41986         }
41987         
41988         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
41989        
41990         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
41991         eval(body);
41992         
41993         return this;
41994     },
41995
41996     applyTemplate : function(values){
41997         return this.master.compiled.call(this, values, {});
41998         //var s = this.subs;
41999     },
42000
42001     apply : function(){
42002         return this.applyTemplate.apply(this, arguments);
42003     }
42004
42005  });
42006
42007 Roo.XTemplate.from = function(el){
42008     el = Roo.getDom(el);
42009     return new Roo.XTemplate(el.value || el.innerHTML);
42010 };